All endpoints

List enrollments for this cadence

GEThttps://myagentmail.com/cadences/{id}/enrollments

Returns enrollments newest-first, with per-row step-run summary so you can render a "3 of 5 ✅" progress + "Blocked: " 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.
  • leadProfileUrnurn: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:

  • stateactive | 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

{
  "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
required
string

Query parameters

state
"active" | "paused" | "completed" | "replied" | "accepted" | "bounced" | "unenrolled" | "error"

Filter by enrollment state.

limit
integer

Responses

200application/jsonEnrollment list (with per-row step-run summary).
ok
boolean
enrollments
object[]
id
string

format: uuid

cadenceId
string

format: uuid

leadExternalId
string
leadName
string
leadEmail
string
leadLinkedinUrl
string
leadProfileUrn
string

Set by the runner after the first LinkedIn invite resolves. Null until then.

sessionId
string
inboxId
string
currentStep
integer

0-based index of the NEXT step due.

state
"active" | "paused" | "completed" | "replied" | "accepted" | "bounced" | "unenrolled" | "error"

enum: "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

format: date-time

lastStepFiredAt
string

format: date-time

lastError
object

Most recent FAILED step run, or null. `code` values come from the cadence runner — see the description above for the catalogue.

code
string
message
string
atStepIndex
integer
firedAt
string

format: date-time

exitReason
string
exitAt
string

format: date-time

lastInvitationUrn
string
context
object
enrolledAt
string

format: date-time

updatedAt
string

format: date-time

Authentication

Send your API key in the X-API-Key header (or Authorization: Bearer <key>). Any prefix accepted by this endpoint — tk_, wk_, ak_, or sa_ — is documented in the key prefix table.