---
title: "List enrollments for this cadence"
method: GET
path: "/cadences/{id}/enrollments"
tags: ["Cadences"]
---

# List enrollments for this cadence

`GET /cadences/{id}/enrollments`

## Request

- Base URL: `https://anjal.app`
- URL: `https://anjal.app/cadences/{id}/enrollments`
- Auth: API key in header `X-API-Key`

Returns enrollments newest-first, with per-row step-run summary so
you can render a "3 of 5 ✅" progress + "Blocked: <reason>" badge
in one HTTP call — no N+1 fetch into `/v1/enrollments/{id}`.

## What you get per enrollment

Identity (set at enroll time, returned verbatim):

- `leadExternalId`     — your idempotency key
- `leadName`, `leadEmail`, `leadLinkedinUrl` — whatever you sent
                           on enrollment. Returned even if null.
- `leadProfileUrn`     — `urn:li:fsd_profile:…`, set by the runner
                           after the first LinkedIn invite resolves
                           the profile. Null until then.
- `sessionId`          — LinkedIn session the cadence runs through.
                           May be auto-picked from the workspace pool
                           if you didn't pass one (see commit f0d2ff1)
                           — once picked, it's pinned to the enrollment.

State + progress:

- `state`              — `active | paused | completed | replied |
                           accepted | bounced | unenrolled | error`
- `currentStep`        — 0-based index of the NEXT step due
- `stepsCompleted`     — count of DISTINCT step indices that have
                           fired with `status='sent'`. Visual: green
                           checkmark count.
- `nextStepDueAt`      — when the runner will next consider this row
                           (ISO 8601). Past time = overdue + queued.
- `lastStepFiredAt`    — ISO 8601 of the most recent step run.
- `lastError`          — most recent FAILED step run (status=
                           `failed`), or null if no failures. Keys:
                           `code`, `message`, `atStepIndex`, `firedAt`.
                           The `code` values match what `runner.ts`
                           writes — most common today are
                           `NO_SESSION_AVAILABLE`,
                           `WORKSPACE_QUOTA_EXHAUSTED`,
                           `PINNED_SESSION_UNUSABLE`, `NO_TARGET`,
                           `NO_INBOX`, `DRAFT_WEBHOOK_FAILED`
                           (your draft handler timed out, 5xx'd,
                           or returned malformed JSON), and
                           `AI_STRATEGY_REMOVED` (pre-existing
                           step still on `draftStrategy: "ai"`
                           — update the step to `static` or
                           `webhook`). Skipped steps (a step
                           whose condition didn't apply — e.g.
                           `after_accept` on a rejected invite,
                           or your draft handler returned
                           `{ skip: true }`) are NOT counted as
                           errors; they're a normal part of
                           cadence execution.

## Worked example response

```json
{
  "ok": true,
  "enrollments": [
    {
      "id": "01HZ…",
      "cadenceId": "01HY…",
      "leadExternalId": "delegated:lead:abc-123",
      "leadName": "Jane Doe",
      "leadEmail": "jane@acme.com",
      "leadLinkedinUrl": "https://www.linkedin.com/in/jane-doe-1a2b3c/",
      "leadProfileUrn": "urn:li:fsd_profile:ACoAAA…aaaa",
      "sessionId": "01HX…",
      "inboxId": "01HW…",
      "currentStep": 3,
      "state": "active",
      "stepsCompleted": 2,
      "nextStepDueAt": "2026-05-21T14:00:00Z",
      "lastStepFiredAt": "2026-05-19T14:00:00Z",
      "lastError": null,
      "enrolledAt": "2026-05-18T10:30:00Z",
      "updatedAt": "2026-05-19T14:00:00Z"
    },
    {
      "id": "01HZ…",
      "cadenceId": "01HY…",
      "leadExternalId": "delegated:lead:def-456",
      "leadName": "John Smith",
      "leadEmail": null,
      "leadLinkedinUrl": "https://www.linkedin.com/in/john-smith/",
      "leadProfileUrn": null,
      "sessionId": null,
      "inboxId": null,
      "currentStep": 0,
      "state": "active",
      "stepsCompleted": 0,
      "nextStepDueAt": "2026-05-19T13:00:00Z",
      "lastStepFiredAt": "2026-05-19T13:00:00Z",
      "lastError": {
        "code": "NO_SESSION_AVAILABLE",
        "message": "no_active_linkedin_session_in_workspace — connect a LinkedIn account from /dashboard/linkedin to start running cadence steps",
        "atStepIndex": 0,
        "firedAt": "2026-05-19T13:00:00Z"
      },
      "enrolledAt": "2026-05-19T12:00:00Z",
      "updatedAt": "2026-05-19T13:00:00Z"
    }
  ]
}
```

## Drilling into one enrollment

For per-step ✅ render (the full `stepRuns[]` history, including
skipped steps, rendered message text, channel URNs), call
`GET /v1/enrollments/{id}` — that endpoint returns the same
enrollment fields plus a `stepRuns` array of the last 50 runs
ordered newest-first.

## Path parameters

- `id` string, uuid, required

## Query parameters

- `state` 'active' | 'paused' | 'completed' | 'replied' | 'accepted' | 'bounced' | 'unenrolled' | 'error'
- `limit` integer

## Response `200`

Enrollment list (with per-row step-run summary).

- object
  - `ok` boolean
  - `enrollments` object[]
    - `id` string, uuid
    - `cadenceId` string, uuid
    - `leadExternalId` string
    - `leadName` string, nullable
    - `leadEmail` string, nullable
    - `leadLinkedinUrl` string, nullable
    - `leadProfileUrn` string, nullable: Set by the runner after the first LinkedIn invite resolves. Null until then.
    - `sessionId` string, nullable
    - `inboxId` string, nullable
    - `currentStep` integer: 0-based index of the NEXT step due.
    - `state` 'active' | 'paused' | 'completed' | 'replied' | 'accepted' | 'bounced' | 'unenrolled' | 'error'
    - `stepsCompleted` integer: Count of DISTINCT step indices that have fired with `status='sent'`. Skipped + failed steps do NOT count. Visual: green-checkmark count.
    - `nextStepDueAt` string, date-time, nullable
    - `lastStepFiredAt` string, date-time, nullable
    - `lastError` object, nullable: Most recent FAILED step run, or null. `code` values come from the cadence runner — see the description above for the catalogue.
      - `code` string, nullable
      - `message` string, nullable
      - `atStepIndex` integer
      - `firedAt` string, date-time
    - `exitReason` string, nullable
    - `exitAt` string, date-time, nullable
    - `lastInvitationUrn` string, nullable
    - `context` object, nullable
    - `enrolledAt` string, date-time
    - `updatedAt` string, date-time

---

[API](https://skmtc.dev/anjal/apis/anjal-api.md) · [All operations](https://skmtc.dev/anjal/apis/anjal-api/llms.txt) · [OpenAPI document](https://skmtc.dev/anjal/apis/anjal-api/revisions/a4044778e294?raw)
