> For the complete documentation index, see [llms.txt](https://docs.zero.inc/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.zero.inc/features/api/sequence-enrollments.md).

# Sequence Enrollments

Enroll contacts into a sequence and track their progress.

#### Enrolling a contact

A sequence owns a **source list** (its `listId`). A contact is enrolled by adding that list to the contact's `listIds` array — there is no separate "enroll" endpoint:

```bash
# 1. Find the sequence's source list id
curl -G "https://api.zero.inc/api/sequences/SEQUENCE_UUID" \
  --data-urlencode "fields=id,name,listId" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

# 2. Add that list to the contact's listIds — this enrolls them.
#    listIds is replaced wholesale, so include the contact's existing lists too.
curl -X PATCH "https://api.zero.inc/api/contacts/CONTACT_UUID" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"listIds": ["EXISTING_LIST_UUID", "SEQUENCE_LIST_UUID"]}'
```

The contact immediately gets an enrollment record (a `SequenceContactStatus`) with status `waitingForPickUp`. If the sequence is **active**, a background worker assigns the first step and begins sending within about a minute. Sequences whose start step uses manual roll-out land in `waitingForApproval` until approved in the app.

#### Unenrolling

Remove the sequence's list from the contact's `listIds`. The enrollment's status flips to `cancelled` and no further steps run.

#### Unsubscribing from all messaging

Unenrolling only stops *this* sequence. To suppress **all** outbound messaging to a contact — across every sequence, automation, and agent send — set `unsubscribedFromAllMessaging: true` via `PATCH /api/contacts/{contactId}` (see the `ContactUpdate` schema). New enrollments are then blocked and any active run is stopped on its next step, with an outcome of `unsubscribed`.

#### Tracking progress

Use `GET /api/sequenceContactStatuses` for per-contact state, and `GET /api/sequences/progressSummary` for aggregate per-sequence metrics. `POST /api/sequences/reenroll` resets contacts to the start; `POST /api/sequences/forceNextStep` advances them.

## Re-enroll contacts

> Resets the given contacts to the start of the sequence (status \`waitingForPickUp\`), clearing their step history, outcome, scheduled emails, enrollment logs, and any AI drafts. Contacts with no existing enrollment row are enrolled fresh. The background worker re-assigns the first step on the next pass (if the sequence is active).<br>

````json
{"openapi":"3.0.3","info":{"title":"Zero API","version":"1.12.0"},"tags":[{"name":"Sequence Enrollments","description":"Enroll contacts into a sequence and track their progress.\n\n### Enrolling a contact\nA sequence owns a **source list** (its `listId`). A contact is enrolled by adding that list to the contact's `listIds` array — there is no separate \"enroll\" endpoint:\n\n```bash\n# 1. Find the sequence's source list id\ncurl -G \"https://api.zero.inc/api/sequences/SEQUENCE_UUID\" \\\n  --data-urlencode \"fields=id,name,listId\" \\\n  -H \"Authorization: Bearer YOUR_API_TOKEN\"\n\n# 2. Add that list to the contact's listIds — this enrolls them.\n#    listIds is replaced wholesale, so include the contact's existing lists too.\ncurl -X PATCH \"https://api.zero.inc/api/contacts/CONTACT_UUID\" \\\n  -H \"Authorization: Bearer YOUR_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"listIds\": [\"EXISTING_LIST_UUID\", \"SEQUENCE_LIST_UUID\"]}'\n```\n\nThe contact immediately gets an enrollment record (a `SequenceContactStatus`) with status `waitingForPickUp`. If the sequence is **active**, a background worker assigns the first step and begins sending within about a minute. Sequences whose start step uses manual roll-out land in `waitingForApproval` until approved in the app.\n\n### Unenrolling\nRemove the sequence's list from the contact's `listIds`. The enrollment's status flips to `cancelled` and no further steps run.\n\n### Unsubscribing from all messaging\nUnenrolling only stops *this* sequence. To suppress **all** outbound messaging to a contact — across every sequence, automation, and agent send — set `unsubscribedFromAllMessaging: true` via `PATCH /api/contacts/{contactId}` (see the `ContactUpdate` schema). New enrollments are then blocked and any active run is stopped on its next step, with an outcome of `unsubscribed`.\n\n### Tracking progress\nUse `GET /api/sequenceContactStatuses` for per-contact state, and `GET /api/sequences/progressSummary` for aggregate per-sequence metrics. `POST /api/sequences/reenroll` resets contacts to the start; `POST /api/sequences/forceNextStep` advances them.\n"}],"servers":[{"url":"https://api.zero.inc","description":"Production server"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"All API requests require a Bearer token in the Authorization header. Create an API key from [Workspace Settings → API keys](https://app.zero.inc/settings/workspace/api)"}},"responses":{"Unauthorized":{"description":"Authentication failed or token is invalid/expired","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"NotFound":{"description":"The requested resource was not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"paths":{"/api/sequences/reenroll":{"post":{"tags":["Sequence Enrollments"],"summary":"Re-enroll contacts","description":"Resets the given contacts to the start of the sequence (status `waitingForPickUp`), clearing their step history, outcome, scheduled emails, enrollment logs, and any AI drafts. Contacts with no existing enrollment row are enrolled fresh. The background worker re-assigns the first step on the next pass (if the sequence is active).\n","operationId":"reenrollSequenceContacts","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["sequenceId","contactIds"],"properties":{"sequenceId":{"type":"string","format":"uuid"},"contactIds":{"type":"array","items":{"type":"string","format":"uuid"}}}}}}},"responses":{"200":{"description":"Contacts re-enrolled","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"count":{"type":"integer","description":"Number of contacts processed."}}}}}},"400":{"description":"sequenceId or contactIds missing, or the sequence has no source list"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}}}}
````

## Advance contacts to the next step

> Moves the given contacts from their current step to the next one immediately, cancelling any pending scheduled emails for the current step. Contacts already on the last step are marked \`completed\`; contacts with no current step are skipped. When the next step is a \`delay\`, the contact parks on the delay and resumes naturally when it elapses.<br>

````json
{"openapi":"3.0.3","info":{"title":"Zero API","version":"1.12.0"},"tags":[{"name":"Sequence Enrollments","description":"Enroll contacts into a sequence and track their progress.\n\n### Enrolling a contact\nA sequence owns a **source list** (its `listId`). A contact is enrolled by adding that list to the contact's `listIds` array — there is no separate \"enroll\" endpoint:\n\n```bash\n# 1. Find the sequence's source list id\ncurl -G \"https://api.zero.inc/api/sequences/SEQUENCE_UUID\" \\\n  --data-urlencode \"fields=id,name,listId\" \\\n  -H \"Authorization: Bearer YOUR_API_TOKEN\"\n\n# 2. Add that list to the contact's listIds — this enrolls them.\n#    listIds is replaced wholesale, so include the contact's existing lists too.\ncurl -X PATCH \"https://api.zero.inc/api/contacts/CONTACT_UUID\" \\\n  -H \"Authorization: Bearer YOUR_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"listIds\": [\"EXISTING_LIST_UUID\", \"SEQUENCE_LIST_UUID\"]}'\n```\n\nThe contact immediately gets an enrollment record (a `SequenceContactStatus`) with status `waitingForPickUp`. If the sequence is **active**, a background worker assigns the first step and begins sending within about a minute. Sequences whose start step uses manual roll-out land in `waitingForApproval` until approved in the app.\n\n### Unenrolling\nRemove the sequence's list from the contact's `listIds`. The enrollment's status flips to `cancelled` and no further steps run.\n\n### Unsubscribing from all messaging\nUnenrolling only stops *this* sequence. To suppress **all** outbound messaging to a contact — across every sequence, automation, and agent send — set `unsubscribedFromAllMessaging: true` via `PATCH /api/contacts/{contactId}` (see the `ContactUpdate` schema). New enrollments are then blocked and any active run is stopped on its next step, with an outcome of `unsubscribed`.\n\n### Tracking progress\nUse `GET /api/sequenceContactStatuses` for per-contact state, and `GET /api/sequences/progressSummary` for aggregate per-sequence metrics. `POST /api/sequences/reenroll` resets contacts to the start; `POST /api/sequences/forceNextStep` advances them.\n"}],"servers":[{"url":"https://api.zero.inc","description":"Production server"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"All API requests require a Bearer token in the Authorization header. Create an API key from [Workspace Settings → API keys](https://app.zero.inc/settings/workspace/api)"}},"responses":{"Unauthorized":{"description":"Authentication failed or token is invalid/expired","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"NotFound":{"description":"The requested resource was not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"paths":{"/api/sequences/forceNextStep":{"post":{"tags":["Sequence Enrollments"],"summary":"Advance contacts to the next step","description":"Moves the given contacts from their current step to the next one immediately, cancelling any pending scheduled emails for the current step. Contacts already on the last step are marked `completed`; contacts with no current step are skipped. When the next step is a `delay`, the contact parks on the delay and resumes naturally when it elapses.\n","operationId":"forceNextSequenceStep","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["sequenceId","contactIds"],"properties":{"sequenceId":{"type":"string","format":"uuid"},"contactIds":{"type":"array","items":{"type":"string","format":"uuid"}}}}}}},"responses":{"200":{"description":"Contacts advanced","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"advanced":{"type":"integer"},"completed":{"type":"integer"},"skipped":{"type":"integer"}}}}}},"400":{"description":"sequenceId or contactIds missing"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}}}}
````

## Sequence progress metrics

> Returns aggregate enrollment metrics per sequence for a workspace (enrolled, active, paused, completed, replied, bounced, etc.). Pass \`sequenceId\` to scope the summary to a single sequence.<br>

````json
{"openapi":"3.0.3","info":{"title":"Zero API","version":"1.12.0"},"tags":[{"name":"Sequence Enrollments","description":"Enroll contacts into a sequence and track their progress.\n\n### Enrolling a contact\nA sequence owns a **source list** (its `listId`). A contact is enrolled by adding that list to the contact's `listIds` array — there is no separate \"enroll\" endpoint:\n\n```bash\n# 1. Find the sequence's source list id\ncurl -G \"https://api.zero.inc/api/sequences/SEQUENCE_UUID\" \\\n  --data-urlencode \"fields=id,name,listId\" \\\n  -H \"Authorization: Bearer YOUR_API_TOKEN\"\n\n# 2. Add that list to the contact's listIds — this enrolls them.\n#    listIds is replaced wholesale, so include the contact's existing lists too.\ncurl -X PATCH \"https://api.zero.inc/api/contacts/CONTACT_UUID\" \\\n  -H \"Authorization: Bearer YOUR_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"listIds\": [\"EXISTING_LIST_UUID\", \"SEQUENCE_LIST_UUID\"]}'\n```\n\nThe contact immediately gets an enrollment record (a `SequenceContactStatus`) with status `waitingForPickUp`. If the sequence is **active**, a background worker assigns the first step and begins sending within about a minute. Sequences whose start step uses manual roll-out land in `waitingForApproval` until approved in the app.\n\n### Unenrolling\nRemove the sequence's list from the contact's `listIds`. The enrollment's status flips to `cancelled` and no further steps run.\n\n### Unsubscribing from all messaging\nUnenrolling only stops *this* sequence. To suppress **all** outbound messaging to a contact — across every sequence, automation, and agent send — set `unsubscribedFromAllMessaging: true` via `PATCH /api/contacts/{contactId}` (see the `ContactUpdate` schema). New enrollments are then blocked and any active run is stopped on its next step, with an outcome of `unsubscribed`.\n\n### Tracking progress\nUse `GET /api/sequenceContactStatuses` for per-contact state, and `GET /api/sequences/progressSummary` for aggregate per-sequence metrics. `POST /api/sequences/reenroll` resets contacts to the start; `POST /api/sequences/forceNextStep` advances them.\n"}],"servers":[{"url":"https://api.zero.inc","description":"Production server"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"All API requests require a Bearer token in the Authorization header. Create an API key from [Workspace Settings → API keys](https://app.zero.inc/settings/workspace/api)"}},"schemas":{"SequenceProgressSummaryEntry":{"type":"object","description":"Aggregate enrollment metrics for a single sequence.","properties":{"sequenceId":{"type":"string","format":"uuid"},"enrolled":{"type":"integer","description":"Total non-archived enrollments."},"waitingForPickUp":{"type":"integer"},"waitingForApproval":{"type":"integer"},"active":{"type":"integer"},"paused":{"type":"integer"},"statusCompleted":{"type":"integer","description":"Enrollments that reached the end of the sequence."},"stopped":{"type":"integer"},"cancelled":{"type":"integer"},"replied":{"type":"integer"},"bounced":{"type":"integer"},"unsubscribed":{"type":"integer"},"error":{"type":"integer","description":"Enrollments whose outcome was specifically `error` (delivery/processing failure)."},"finished":{"type":"integer","description":"Completed + stopped + cancelled."},"activeOrScheduled":{"type":"integer"},"errors":{"type":"integer","description":"All failed-delivery outcomes combined — `error` + `bounced` + `unsubscribed`."},"responseRatePercent":{"type":"number"}}}},"responses":{"Unauthorized":{"description":"Authentication failed or token is invalid/expired","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"paths":{"/api/sequences/progressSummary":{"get":{"tags":["Sequence Enrollments"],"summary":"Sequence progress metrics","description":"Returns aggregate enrollment metrics per sequence for a workspace (enrolled, active, paused, completed, replied, bounced, etc.). Pass `sequenceId` to scope the summary to a single sequence.\n","operationId":"getSequenceProgressSummary","parameters":[{"name":"workspaceId","in":"query","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"sequenceId","in":"query","required":false,"description":"Limit the summary to a single sequence.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"data":{"type":"array","items":{"$ref":"#/components/schemas/SequenceProgressSummaryEntry"}}}}}}},"400":{"description":"workspaceId missing"},"401":{"$ref":"#/components/responses/Unauthorized"}}}}}}
````

## List enrollments

> Returns enrollment records (\`SequenceContactStatus\`) — one per contact per sequence — with each contact's live status, current step, and outcome. Filter with \`where\`, e.g. \`{"workspaceId": "\<WORKSPACE\_UUID>", "sequenceId": "\<SEQUENCE\_UUID>"}\`, optionally adding \`"status": "active"\`.<br>

````json
{"openapi":"3.0.3","info":{"title":"Zero API","version":"1.12.0"},"tags":[{"name":"Sequence Enrollments","description":"Enroll contacts into a sequence and track their progress.\n\n### Enrolling a contact\nA sequence owns a **source list** (its `listId`). A contact is enrolled by adding that list to the contact's `listIds` array — there is no separate \"enroll\" endpoint:\n\n```bash\n# 1. Find the sequence's source list id\ncurl -G \"https://api.zero.inc/api/sequences/SEQUENCE_UUID\" \\\n  --data-urlencode \"fields=id,name,listId\" \\\n  -H \"Authorization: Bearer YOUR_API_TOKEN\"\n\n# 2. Add that list to the contact's listIds — this enrolls them.\n#    listIds is replaced wholesale, so include the contact's existing lists too.\ncurl -X PATCH \"https://api.zero.inc/api/contacts/CONTACT_UUID\" \\\n  -H \"Authorization: Bearer YOUR_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"listIds\": [\"EXISTING_LIST_UUID\", \"SEQUENCE_LIST_UUID\"]}'\n```\n\nThe contact immediately gets an enrollment record (a `SequenceContactStatus`) with status `waitingForPickUp`. If the sequence is **active**, a background worker assigns the first step and begins sending within about a minute. Sequences whose start step uses manual roll-out land in `waitingForApproval` until approved in the app.\n\n### Unenrolling\nRemove the sequence's list from the contact's `listIds`. The enrollment's status flips to `cancelled` and no further steps run.\n\n### Unsubscribing from all messaging\nUnenrolling only stops *this* sequence. To suppress **all** outbound messaging to a contact — across every sequence, automation, and agent send — set `unsubscribedFromAllMessaging: true` via `PATCH /api/contacts/{contactId}` (see the `ContactUpdate` schema). New enrollments are then blocked and any active run is stopped on its next step, with an outcome of `unsubscribed`.\n\n### Tracking progress\nUse `GET /api/sequenceContactStatuses` for per-contact state, and `GET /api/sequences/progressSummary` for aggregate per-sequence metrics. `POST /api/sequences/reenroll` resets contacts to the start; `POST /api/sequences/forceNextStep` advances them.\n"}],"servers":[{"url":"https://api.zero.inc","description":"Production server"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"All API requests require a Bearer token in the Authorization header. Create an API key from [Workspace Settings → API keys](https://app.zero.inc/settings/workspace/api)"}},"parameters":{"fields":{"name":"fields","in":"query","description":"Comma-separated list of fields to return. Defaults to all fields.","schema":{"type":"string"}},"where":{"name":"where","in":"query","description":"JSON-encoded filter object. All top-level conditions are combined with AND logic. Use `$or` for OR logic.\n\nThe available operators depend on the **data type** of the field you are filtering on.\n\n---\n\n## String fields\nFields like `name`, `domain`, `description`, `linkedin`, `source`, `externalId`, `location`.\n\n| Operator | Description | Example |\n|----------|-------------|---------|\n| *(exact)* | Exact match | `{\"name\": \"Linear\"}` |\n| `$eq` | Explicit exact match | `{\"name\": {\"$eq\": \"Linear\"}}` |\n| `$not` | Not equal | `{\"source\": {\"$not\": \"import\"}}` |\n| `$in` | Matches any value in array | `{\"domain\": {\"$in\": [\"linear.app\", \"granola.so\"]}}` |\n| `$notIn` | Matches none of the values | `{\"source\": {\"$notIn\": [\"import\", \"api\"]}}` |\n| `$contains` | Case-insensitive word-boundary substring match | `{\"name\": {\"$contains\": \"YC\"}}` |\n| `$notContains` | Does not contain | `{\"name\": {\"$notContains\": \"Test\"}}` |\n| `$containsAny` | Contains any of the given strings | `{\"name\": {\"$containsAny\": [\"YC\", \"Techstars\"]}}` |\n| `$startsWith` | Starts with prefix | `{\"domain\": {\"$startsWith\": \"app.\"}}` |\n| `$endsWith` | Ends with suffix | `{\"email\": {\"$endsWith\": \"@zero.inc\"}}` |\n| `$exists` | Field is present and truthy | `{\"linkedin\": {\"$exists\": true}}` |\n| `$notExists` | Field is absent, null, or empty | `{\"linkedin\": {\"$notExists\": true}}` |\n\n---\n\n## Number fields\nFields like `value`, `confidence`.\n\n| Operator | Description | Example |\n|----------|-------------|---------|\n| *(exact)* | Exact match | `{\"value\": 5000}` |\n| `$eq` | Explicit exact match | `{\"value\": {\"$eq\": 5000}}` |\n| `$not` | Not equal | `{\"value\": {\"$not\": 0}}` |\n| `$gt` | Greater than | `{\"value\": {\"$gt\": 10000}}` |\n| `$gte` | Greater than or equal | `{\"value\": {\"$gte\": 5000}}` |\n| `$lt` | Less than | `{\"value\": {\"$lt\": 10000}}` |\n| `$lte` | Less than or equal | `{\"value\": {\"$lte\": 50000}}` |\n| `$in` | Matches any value in array | `{\"confidence\": {\"$in\": [0.25, 0.5, 0.75]}}` |\n| `$notIn` | Matches none of the values | `{\"confidence\": {\"$notIn\": [0, 1]}}` |\n| `$exists` | Field is present and truthy | `{\"value\": {\"$exists\": true}}` |\n| `$notExists` | Field is absent, null, or zero | `{\"value\": {\"$notExists\": true}}` |\n\nMultiple operators can be combined on one field:\n```json\n{\"value\": {\"$gte\": 5000, \"$lt\": 10000}}\n```\n\n---\n\n## Date fields\nFields like `closeDate`, `startDate`, `endDate`, `createdAt`, `updatedAt`.\n\nValues can be ISO 8601 strings (`\"2026-01-01\"`, `\"2026-01-01T00:00:00Z\"`) or **relative time macros**.\n\n**Relative time macros:** `+Nd` / `-Nd` (days), `+Nw` / `-Nw` (weeks), `+Nm` / `-Nm` (months), `+Ny` / `-Ny` (years), `+Nh` / `-Nh` (hours), `+Ns` / `-Ns` (seconds), `now()`.\n\n| Operator | Description | Example |\n|----------|-------------|---------|\n| `$gte` | On or after | `{\"closeDate\": {\"$gte\": \"2026-01-01\"}}` |\n| `$lte` | On or before | `{\"closeDate\": {\"$lte\": \"2026-03-31\"}}` |\n| `$gt` | After | `{\"createdAt\": {\"$gt\": \"2026-01-01T00:00:00Z\"}}` |\n| `$lt` | Before | `{\"createdAt\": {\"$lt\": \"now()\"}}` |\n| `$date` | Exact date match (compares date portion only) | `{\"closeDate\": {\"$date\": \"2026-01-15\"}}` |\n| `$exists` | Field is present and truthy | `{\"closeDate\": {\"$exists\": true}}` |\n| `$notExists` | Field is absent or null | `{\"closeDate\": {\"$notExists\": true}}` |\n\nDate range example:\n```json\n{\"closeDate\": {\"$gte\": \"2026-01-01\", \"$lte\": \"2026-03-31\"}}\n```\nRelative date example (closing in next 30 days):\n```json\n{\"closeDate\": {\"$gte\": \"now()\", \"$lte\": \"+30d\"}}\n```\n\n---\n\n## Array fields\nFields like `listIds`, `ownerIds`, `contactIds`.\n\n| Operator | Description | Example |\n|----------|-------------|---------|\n| `$includes` | Array contains the given value (use this — bare exact match is not supported) | `{\"listIds\": {\"$includes\": \"<LIST_UUID>\"}}` |\n| `$notIncludes` | Array does not contain the given value | `{\"ownerIds\": {\"$notIncludes\": \"<USER_UUID>\"}}` |\n| `$overlaps` | Array contains at least one of the given values | `{\"ownerIds\": {\"$overlaps\": [\"<UUID_1>\", \"<UUID_2>\"]}}` |\n| `$notOverlaps` | Array contains none of the given values | `{\"listIds\": {\"$notOverlaps\": [\"<UUID_1>\", \"<UUID_2>\"]}}` |\n| `$all` | Array contains all of the given values | `{\"listIds\": {\"$all\": [\"<UUID_1>\", \"<UUID_2>\"]}}` |\n| `$length` | Filter by array length (supports nested operators) | `{\"contactIds\": {\"$length\": {\"$gte\": 2}}}` |\n| `$exists` | Field is present and non-empty | `{\"ownerIds\": {\"$exists\": true}}` |\n| `$notExists` | Field is absent or empty | `{\"ownerIds\": {\"$notExists\": true}}` |\n\n---\n\n## UUID / ID fields\nFields like `id`, `workspaceId`, `companyId`, `stage`.\n\n| Operator | Description | Example |\n|----------|-------------|---------|\n| *(exact)* | Exact match | `{\"stage\": \"<PIPELINE_STAGE_UUID>\"}` |\n| `$in` | Matches any value in array | `{\"stage\": {\"$in\": [\"<UUID_1>\", \"<UUID_2>\"]}}` |\n| `$notIn` | Matches none of the values | `{\"stage\": {\"$notIn\": [\"<UUID_1>\"]}}` |\n| `$not` | Not equal | `{\"stage\": {\"$not\": \"<UUID>\"}}` |\n\n---\n\n## Boolean fields\nFields like `archived`.\n\n| Operator | Description | Example |\n|----------|-------------|---------|\n| *(exact)* | Exact match | `{\"archived\": false}` |\n| `$not` | Not equal | `{\"archived\": {\"$not\": true}}` |\n\n---\n\n## Logical operators\nThese work across all data types.\n\n`$or` — matches if **any** sub-condition is true:\n```json\n{\n  \"workspaceId\": \"<WORKSPACE_UUID>\",\n  \"$or\": [\n    {\"ownerIds\": {\"$overlaps\": [\"<USER_UUID_1>\", \"<USER_UUID_2>\"]}},\n    {\"closeDate\": {\"$gte\": \"2026-01-01\"}}\n  ]\n}\n```\n\n`$and` — matches if **all** sub-conditions are true (useful when you need multiple conditions on the same field):\n```json\n{\n  \"$and\": [\n    {\"name\": {\"$contains\": \"Enterprise\"}},\n    {\"name\": {\"$notContains\": \"Test\"}}\n  ]\n}\n```\n\n---\n\n## Dot-notation (relation filtering)\nUse dot-syntax to filter records based on properties of related objects:\n```json\n{\"company.name\": \"Linear\"}\n{\"company.domain\": {\"$in\": [\"linear.app\", \"granola.so\"]}}\n{\"company.location.city\": \"San Francisco\"}\n{\"companyProfile.categories\": {\"$overlaps\": [\"Sales\", \"Marketing\"]}}\n```\nAll operators available for the target field's data type can be used with dot-notation.\n\n---\n\n## Custom property filtering\nCustom 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.\n\nFilter on custom properties using `custom.<COLUMN_ID>` with operators appropriate for the column's type.\n\n> **Note:** For `select` and `multiselect` columns, option values are UUIDs (the `key` field from the column's `options` array). Use the UUID, not the human-readable name.\n\n```json\n// Text custom property\n{\"custom.54e1ca7d-69c3-4b77-8266-8085b5834116\": {\"$contains\": \"enterprise\"}}\n\n// Select custom property (use the option key UUID)\n{\"custom.a1b2c3d4-e5f6-7890-abcd-ef1234567890\": \"3e839b5c-b311-4887-b2da-727d2d75cdd6\"}\n\n// Multi-select custom property (use option key UUIDs)\n{\"custom.b2c3d4e5-f6a7-8901-bcde-f12345678901\": {\"$overlaps\": [\"a1b1c1d1-e1f1-1111-aaaa-111111111111\", \"b2b2c2d2-e2f2-2222-bbbb-222222222222\"]}}\n\n// Currency/number custom property\n{\"custom.c3d4e5f6-a7b8-9012-cdef-123456789012\": {\"$gte\": 100000}}\n\n// Date custom property\n{\"custom.d4e5f6a7-b8c9-0123-defa-234567890123\": {\"$gte\": \"2026-01-01\"}}\n\n// Boolean custom property\n{\"custom.e5f6a7b8-c9d0-1234-efab-345678901234\": true}\n\n// Check if custom property has a value\n{\"custom.f6a7b8c9-d0e1-2345-fabc-456789012345\": {\"$exists\": true}}\n```\n\n---\n\n## Complex example\nAll top-level keys are ANDed together:\n```json\n{\n  \"name\": {\"$contains\": \"Zero\"},\n  \"location.city\": \"Helsinki\",\n  \"location.country\": {\"$in\": [\"United Kingdom\", \"Germany\", \"Sweden\"]},\n  \"value\": {\"$gte\": 10000},\n  \"closeDate\": {\"$gte\": \"2026-01-01\", \"$lte\": \"2026-03-31\"},\n  \"ownerIds\": {\"$includes\": \"<USER_UUID>\"},\n  \"stage\": {\"$in\": [\"<UUID_1>\", \"<UUID_2>\"]},\n  \"lastActivity\": {\"$exists\": true},\n  \"companyProfile.categories\": {\"$overlaps\": [\"Sales\", \"Marketing\"]},\n  \"custom.54e1ca7d-69c3-4b77-8266-8085b5834116\": {\"$contains\": \"enterprise\"}\n}\n```\n","schema":{"type":"string"}},"limit":{"name":"limit","in":"query","description":"Maximum number of records to return","schema":{"type":"integer","default":100}},"offset":{"name":"offset","in":"query","description":"Pagination offset","schema":{"type":"integer","default":0}},"orderBy":{"name":"orderBy","in":"query","description":"JSON string for sort order","schema":{"type":"string"}}},"schemas":{"SequenceContactStatus":{"type":"object","description":"An enrollment — one contact's progress through one sequence. Created automatically when a contact is added to the sequence's source list.\n","properties":{"id":{"type":"string","format":"uuid"},"workspaceId":{"type":"string","format":"uuid"},"sequenceId":{"type":"string","format":"uuid"},"listId":{"type":"string","format":"uuid","description":"The sequence source list the contact was enrolled through."},"contactId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["waitingForApproval","waitingForPickUp","active","paused","completed","stopped","cancelled"],"description":"`waitingForPickUp` — enrolled, awaiting the background worker.\n`waitingForApproval` — manual roll-out, awaiting approval in the app.\n`active` — in flight. `paused` — sequence paused. `completed` — reached the end.\n`stopped` — ended early (e.g. replied). `cancelled` — removed from the source list.\n"},"outcomeReason":{"type":"string","nullable":true,"enum":["responded","positiveResponse","negativeResponse","automatedResponse","bounced","unsubscribed","error"]},"outcomeStepId":{"type":"string","format":"uuid","nullable":true,"description":"The step the contact was on when the outcome was recorded."},"responseCategorisation":{"type":"string","nullable":true,"enum":["positive","negative","automated"],"description":"How a reply from the contact was categorised, if any."},"errorMessage":{"type":"string","nullable":true,"description":"Populated when the enrollment hit an error."},"currentStepId":{"type":"string","format":"uuid","nullable":true},"waitingOn":{"type":"string","nullable":true,"enum":["delay","emailScheduling","emailSent","linkedinMessageSent","linkedinConnectionRequestSent","linkedinConnectionResponse","sequenceTaskDeadline","sequenceTaskDone"]},"lastCompletedStepId":{"type":"string","format":"uuid","nullable":true},"lastCompletedStepName":{"type":"string","nullable":true},"nextActionAt":{"type":"string","format":"date-time","nullable":true},"startedAt":{"type":"string","format":"date-time","nullable":true},"endedAt":{"type":"string","format":"date-time","nullable":true},"outcomeAt":{"type":"string","format":"date-time","nullable":true},"lastUpdatedAt":{"type":"string","format":"date-time","description":"When the enrollment's state last changed."},"archived":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}},"responses":{"Unauthorized":{"description":"Authentication failed or token is invalid/expired","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}},"paths":{"/api/sequenceContactStatuses":{"get":{"tags":["Sequence Enrollments"],"summary":"List enrollments","description":"Returns enrollment records (`SequenceContactStatus`) — one per contact per sequence — with each contact's live status, current step, and outcome. Filter with `where`, e.g. `{\"workspaceId\": \"<WORKSPACE_UUID>\", \"sequenceId\": \"<SEQUENCE_UUID>\"}`, optionally adding `\"status\": \"active\"`.\n","operationId":"listSequenceContactStatuses","parameters":[{"$ref":"#/components/parameters/fields"},{"$ref":"#/components/parameters/where"},{"$ref":"#/components/parameters/limit"},{"$ref":"#/components/parameters/offset"},{"$ref":"#/components/parameters/orderBy"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SequenceContactStatus"}},"total":{"type":"integer"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}}}}
````


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.zero.inc/features/api/sequence-enrollments.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
