> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sequenzy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Realign Sequence Enrollments

> Move waiting enrollments to the start of a changed sending window without cancelling or re-enrolling them

# Realign Sequence Enrollments

Changing a live sequence's sending window deliberately leaves contacts who are already waiting on an email-bound delay step alone: they keep the wake-up time their delay produced. That means a widened window never reaches them, and a narrowed window pushes them to the **next** allowed day - a full extra cycle on a weekly sequence.

This endpoint pulls those waits forward instead. It never cancels or re-enrolls anyone.

## Guarantees

* A wait only ever moves **earlier**, never later.
* A wait never moves to a different local day in the window's timezone, so nobody changes weekly cycle.
* A wait never moves earlier than now, so nothing is sent retroactively.
* Per-step "wait until weekday" gates are respected: a contact is never moved earlier than the step's own window opening.
* Sequence windows never advance SMS, webhooks, branches, or other non-email actions.
* Running it twice is a no-op. Waits already sitting at their opening report `already_at_window_start`.

## Request

<ParamField path="sequenceId" type="string" required>
  Sequence ID.
</ParamField>

<ParamField body="nodeIds" type="array">
  Step IDs to limit realignment to. Use `nodeIds` from [Get
  Sequence](/api-reference/sequences/get) or `currentNodeId` values from [List
  Enrollments](/api-reference/sequences/list-enrollments). Defaults to every
  step when omitted; when provided, it must contain at least one non-empty ID.
</ParamField>

<ParamField body="subscriberIds" type="array">
  Up to 500 subscriber IDs to limit realignment to. Defaults to every waiting
  contact when omitted; when provided, it must contain at least one non-empty
  ID.
</ParamField>

<ParamField body="cursor" type="string">
  Opaque continuation cursor. When a response has `hasMore: true`, pass its
  `nextCursor` here to continue after the enrollments already scanned.
</ParamField>

<ParamField body="dryRun" type="boolean">
  When `true` (the default), returns the new wait times without writing them.
  Set it to `false` to queue a background apply job.
</ParamField>

```bash theme={null}
curl -X POST "https://api.sequenzy.com/api/v1/sequences/seq_abc123/enrollments/realign-sending-window" \
  -H "Authorization: Bearer seq_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "dryRun": false
  }'
```

## Why an enrollment did not move

`unchangedReasons` counts each reason:

| Reason                    | Meaning                                                                                   |
| ------------------------- | ----------------------------------------------------------------------------------------- |
| `already_at_window_start` | The wait is already at or before that day's opening                                       |
| `already_due`             | The wait has passed and the enrollment is about to resume                                 |
| `day_not_allowed`         | The wait lands on a day the window does not cover, so moving it would change the send day |
| `no_shared_opening`       | The sequence window and the step's weekday gate never overlap before the wait             |
| `no_window`               | Neither the sequence nor the contact's current step defines a window                      |
| `not_email_bound`         | The delay's next action is not an email, so the sequence window does not govern it        |
| `send_retry`              | The email is waiting on a provider retry backoff, which realignment never shortens        |
| `raced`                   | A worker advanced the enrollment between the scan and the write                           |

## Paging through a large sequence

Each job moves at most 1,000 enrollments and inspects at most 20,000. An applied request returns HTTP 202 with a `jobId`; poll `GET /api/v1/sequences/{sequenceId}/enrollments/realign-sending-window/jobs/{jobId}` until it completes. When the completed result has `hasMore: true`, pass its `nextCursor` back as `cursor` to queue the next bounded job.

```bash theme={null}
sequenzy sequences realign-enrollments seq_abc123 --apply
sequenzy sequences realign-enrollments seq_abc123 --job-id <printed-job-id>
sequenzy sequences realign-enrollments seq_abc123 --apply --cursor eyJzY2hlZHVsZWRGb3IiOiIuLi4ifQ
```

## Responses

<ResponseExample>
  ```json 202 theme={null}
  {
    "success": true,
    "sequenceId": "seq_abc123",
    "sequenceName": "52-Week Medicine Wheel",
    "dryRun": false,
    "status": "queued",
    "jobId": "realign-sequence-enrollments-seq_abc123-...",
    "message": "Enrollment realignment queued. Poll the job status endpoint for the result before continuing with nextCursor."
  }
  ```

  ```json 200 theme={null}
  {
    "success": true,
    "sequenceId": "seq_abc123",
    "sequenceName": "52-Week Medicine Wheel",
    "dryRun": true,
    "sendingWindow": {
      "enabled": true,
      "timezone": "America/Los_Angeles",
      "startTime": "08:00",
      "endTime": "12:00",
      "days": ["sunday"]
    },
    "scannedCount": 330,
    "realignedCount": 330,
    "unchangedCount": 0,
    "unchangedReasons": {},
    "requeueFailedCount": 0,
    "changes": [
      {
        "enrollmentId": "tok_abc123",
        "subscriberId": "sub_abc123",
        "subscriberEmail": "customer@example.com",
        "currentNodeId": "node_week_14",
        "waitUntil": "2026-08-16T18:13:00.000Z",
        "newWaitUntil": "2026-08-16T15:00:00.000Z",
        "movedEarlierMinutes": 193
      }
    ],
    "hasMore": false,
    "maxRealignmentsPerRequest": 1000,
    "message": "330 waiting enrollments would move earlier. Re-run with dryRun false to apply."
  }
  ```

  ```json 400 theme={null}
  {
    "error": "A maximum of 500 subscriberIds can be realigned per request"
  }
  ```

  ```json 401 theme={null}
  {
    "error": "Invalid API key"
  }
  ```

  ```json 403 theme={null}
  {
    "error": "API key is missing the sequences:enroll scope"
  }
  ```

  ```json 404 theme={null}
  {
    "error": "Sequence not found"
  }
  ```
</ResponseExample>
