> ## 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.

# Move Sequence Enrollments

> Release a bounded batch of enrolled contacts from one sequence step onto another

# Move Sequence Enrollments

Release a bounded batch of contacts off one sequence step and onto another, keeping their enrollment intact. Use this when you want the next N contacts waiting on a delay to continue early: pace a launch, drain a hold step, or push one wave past a wait without touching the graph.

You could approximate this by cancelling the enrollments and re-enrolling them at the target step, but that throws away the enrollment's entry event properties, its stop-condition entry snapshots, and its original start date, and it is refused outright while the sequence has new enrollment paused. Moving does none of those things.

Moved contacts become `active` on the target step right away, so the sequence emails them as soon as a worker picks them up. Every bulk target defaults to a dry run: pass `dryRun: false` to actually move them.

## Request

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

<ParamField body="fromNodeId" type="string" required>
  Node ID the contacts are currently sitting on. Read it from `currentNodeId` on
  [list enrollments](/api-reference/sequences/list-enrollments).
</ParamField>

<ParamField body="targetNodeId" type="string">
  Node ID to move them onto. Defaults to the source step's only next step, and
  is required when that step branches or is terminal. Cannot be the trigger
  node.
</ParamField>

<ParamField body="limit" type="number">
  Maximum enrollments to move in this call. Defaults to `100`, maximum `500`.
</ParamField>

<ParamField body="sort" type="string">
  Which enrollments to take first: `wait_until_asc` (default),
  `wait_until_desc`, `enrolled_at_asc`, or `enrolled_at_desc`. The default takes
  the contacts that have been waiting longest for their next step.
</ParamField>

<ParamField body="subscriberIds" type="array">
  Optional narrowing filter: only move these subscribers, up to 500.
</ParamField>

<ParamField body="dailyLimit" type="number">
  Guardrail. Refuses to move more than this many enrollments onto `targetNodeId`
  in a rolling 24 hours, counting the moves recorded by earlier calls. The
  response reports `movedInWindow` and `dailyRemaining` so a paced release can
  resume the next day.
</ParamField>

<ParamField body="tags" type="array">
  Tag names applied to the moved contacts so the released wave stays
  identifiable. The tags must already exist, and this requires the
  `subscribers:tag` scope. Applying them never enrolls contacts in `tag_added`
  sequences.
</ParamField>

<ParamField body="reason" type="string">
  Note stored on every moved enrollment and returned as `moveReason` by [list
  enrollments](/api-reference/sequences/list-enrollments).
</ParamField>

<ParamField body="dryRun" type="boolean">
  When `true` (the default), reports which enrollments would move without moving
  them. Pass `false` to apply.
</ParamField>

```bash theme={null}
curl -X POST "https://api.sequenzy.com/api/v1/sequences/seq_abc123/enrollments/move" \
  -H "Authorization: Bearer seq_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "fromNodeId": "node_delay_2",
    "targetNodeId": "node_email_3",
    "limit": 180,
    "dailyLimit": 500,
    "tags": ["wave-3-released"],
    "reason": "Wave 3",
    "dryRun": false
  }'
```

## Pacing a release

Each call is bounded on purpose, so it is never drained automatically. When the response reports `remainingCount` above zero, send the same request again to release the next batch. Combine `limit` with `dailyLimit` to spread a large backlog over several days without tracking the running total yourself.

Active enrollments, enrollments a worker is currently mid-step on, and enrollments parked awaiting double opt-in are never moved. Active tokens may still have older queue work, so only safely parked waiting tokens can be repositioned. Excluded enrollments are reported in `skippedCount` and omitted from `matchedCount`.

```bash theme={null}
# The CLI is deliberately one batch per invocation too.
sequenzy sequences move-enrollments seq_abc123 --from-node node_delay_2 --limit 180 --apply
```

## Responses

<ResponseExample>
  ```json 200 theme={null}
  {
    "success": true,
    "sequenceId": "seq_abc123",
    "dryRun": false,
    "fromNodeId": "node_delay_2",
    "targetNodeId": "node_email_3",
    "sort": "wait_until_asc",
    "requestedLimit": 180,
    "effectiveLimit": 180,
    "matchedCount": 4320,
    "movedCount": 180,
    "remainingCount": 4140,
    "skippedCount": 0,
    "dailyLimit": 500,
    "movedInWindow": 0,
    "dailyRemaining": 320,
    "enqueuedCount": 180,
    "enqueueErrors": [],
    "tagResult": {
      "tags": ["wave-3-released"],
      "updated": 180,
      "unchanged": 0,
      "failed": 0,
      "failures": []
    },
    "enrollments": [
      {
        "tokenId": "tok_abc123",
        "subscriberId": "sub_abc123",
        "subscriberEmail": "customer@example.com",
        "status": "active",
        "enrollmentKey": "__default__"
      }
    ],
    "hasMore": true,
    "message": "Moved 180 enrollments. 4140 still wait on the source step; repeat this request to release more. 320 left under the daily limit."
  }
  ```

  ```json 400 theme={null}
  {
    "error": "fromNodeId branches into multiple next steps, so targetNodeId is required"
  }
  ```

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

  ```json 403 theme={null}
  {
    "error": "Tagging moved enrollments requires the subscribers:tag scope. Omit `tags` to move them without tagging."
  }
  ```

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