# LOC migration procedure

## Assumed knowledge

- [How LOC billing cycles work](/loan-lifecycle/loc-migration/how-loc-billing-cycles-work) — required to understand the balance breakdowns in Step 3.
- [How grace periods work for lines of credit](/loan-lifecycle/loc-migration/how-grace-periods-work) — grace eligibility seeded in the migration period data.
- [How LOC draws work](/loan-lifecycle/loc-migration/how-loc-draws-work) — the migration draw and the active draws used throughout.


## Step 1: Create the borrower and payment methods

Establish the borrower's identity and payment capabilities in Peach.

### Create borrower

Create a borrower record with personal information. You'll use the returned `personId` in all subsequent API calls.


```json
POST /api/people
Content-Type: application/json

{
  "status": "active",
  "externalId": "your-borrower-id-123",
  "name": {
    "firstName": "Jane",
    "middleName": "A",
    "lastName": "Smith"
  },
  "dateOfBirth": "1985-03-15",
  "identity": {
    "identityType": "SSN",
    "value": "123456789"
  }
}
```

**Response** (abbreviated):


```json
{
  "data": {
    "id": "BO-1234-ABCD",
    "externalId": "your-borrower-id-123",
    "status": "active"
  }
}
```

### Create contact records

Create separate contact records for each piece of contact information. This separation enables contact-level management and history tracking.


```json
POST /api/people/{personId}/contacts
Content-Type: application/json

{
  "contactType": "address",
  "label": "home",
  "affiliation": "self",
  "status": "primary",
  "address": {
    "addressLine1": "742 Evergreen Terrace",
    "city": "San Francisco",
    "state": "CA",
    "postalCode": "94105",
    "country": "US"
  }
}
```

Repeat for each email address, phone number, and additional address. Each contact record includes a `contactType` (address, email, phone), a `label` (home, work, mobile), and an `affiliation` (self, spouse, etc.).

### Create payment instruments

Create payment instruments for both active and historical payment methods. Active methods will be used for future payments; historical methods are needed for accurate transaction records.

**Active payment instrument** (full account details):


```json
POST /api/people/{personId}/payment-instruments
Content-Type: application/json

{
  "status": "active",
  "verified": true,
  "nickname": "Primary Checking",
  "instrumentType": "bankAccount",
  "accountNumber": "9876543210",
  "routingNumber": "021000021",
  "accountType": "checking",
  "accountHolderType": "personal",
  "accountHolderName": "Jane A Smith"
}
```

**Historical payment instrument** (last four digits only):

For payment methods no longer in use, set `isExternal: true`. This requires only the last four digits of the account number.


```json
POST /api/people/{personId}/payment-instruments
Content-Type: application/json

{
  "status": "active",
  "verified": true,
  "isExternal": true,
  "nickname": "Old Checking (closed)",
  "instrumentType": "bankAccount",
  "accountNumberLastFour": "5678",
  "accountType": "checking",
  "accountHolderType": "personal",
  "accountHolderName": "Jane A Smith"
}
```

You can also provide inline payment instrument details directly on migration transactions using `paymentInstrumentDetails`, which accepts the following types: `bankAccount`, `creditCard`, `debitCard`, `check`, `payroll`, `moneyOrder`, `wire`, and `custom`.

Only payment instruments with `status: "active"` can be used for future payments. Historical instruments created with `isExternal: true` are for record-keeping only.

## Step 2: Create and configure the line of credit

Create the loan object, retrieve the auto-generated migration draw, create any additional active draws, and upload loan documents.

### Create the loan

Create the line of credit with `status: "pending"` and `migration.migrationStatus: "prepMigration"`. The `atOrigination` object defines the loan's terms; the `migration` object tells Peach this loan is being migrated.


```json
POST /api/people/{personId}/loans
Content-Type: application/json

{
  "externalId": "your-loc-id-789",
  "loanTypeId": "LT-LOC-ABCD",
  "type": "lineOfCredit",
  "servicedBy": "creditor",
  "status": "pending",
  "muteLoanNotices": true,
  "atOrigination": {
    "paymentFrequency": "monthly",
    "specificDays": [22],
    "interestRates": [
      { "days": null, "rate": 0.1999 }
    ],
    "promoRates": [],
    "aprEffective": 0.2149,
    "aprNominal": 0.1999,
    "creditLimitAmount": 10000.00,
    "gracePeriod": {
      "enabled": true,
      "numDays": 25,
      "numPeriodsToRestoreGrace": 1
    },
    "minPaymentCalculation": {
      "percentageOfPrincipal": 0.02,
      "minAmount": 25.00,
      "includeFeesInCalculation": true,
      "includeInterestInCalculation": true
    },
    "personAddressId": "CT-ADDR-ABCD",
    "skipCreditReporting": false
  },
  "migration": {
    "migrationStatus": "prepMigration",
    "activatedDate": "2023-06-15",
    "activatedTimeOfDay": {
      "hour": 10,
      "minute": 0,
      "second": 0
    }
  }
}
```

**Response** (abbreviated):


```json
{
  "data": {
    "id": "LN-1234-ABCD",
    "type": "lineOfCredit",
    "status": "pending",
    "migrationStatus": "prepMigration"
  }
}
```

Setting `muteLoanNotices: true` is not strictly required — Peach will not send borrower notifications during the migration process. However, setting it to `true` provides an extra safeguard and gives you explicit control over when communications resume after migration. See [Borrower communications during migration](/loan-lifecycle/loc-migration/after-migration#re-enable-borrower-communications) for details.

**Key fields in `atOrigination`:**

| Field | Description |
|  --- | --- |
| `paymentFrequency` / `specificDays` | Current billing frequency and due date day(s) of month. `specificDays` must align with the `dueDate` in the migration period data — e.g., if `dueDate` is the 22nd, set `specificDays: [22]`. |
| `interestRates` | Interest rate schedule. Set from the migration cutoff date. Use `days: null` for an indefinite rate. |
| `promoRates` | Promotional rate schedule, if applicable |
| `creditLimitAmount` | Credit limit at origination |
| `gracePeriod` | Grace period configuration. `numDays` is the number of days after the statement date during which no interest accrues if the full balance is paid. |
| `minPaymentCalculation` | Rules for calculating the minimum payment each cycle |
| `skipCreditReporting` | Set to `true` if you do not report this loan to credit bureaus |


**Key fields in `migration`:**

| Field | Description |
|  --- | --- |
| `migrationStatus` | Must be `"prepMigration"` to begin the process |
| `activatedDate` | The original date the line of credit was opened in your legacy system |
| `activatedTimeOfDay` | Time of activation (hour, minute, second in product timezone) |


### Retrieve the migration draw

When you create a loan with `migrationStatus: "prepMigration"`, Peach automatically creates the migration draw. Retrieve it to get its ID — you'll need this when posting historical purchases and fees.


```json
GET /api/people/{personId}/loans/{loanId}/draws
```

**Response** (abbreviated):


```json
{
  "data": [
    {
      "id": "DR-MIGR-ABCD",
      "nickname": "Migration Draw",
      "drawType": "static",
      "status": "pending"
    }
  ]
}
```

The migration draw has `drawType: "static"`. Save this ID for Step 4.

### Create active draws

Create any draws that should exist after the migration cutoff date. These are the "real" draws that will be active post-migration — they accrue interest, accept new purchases, and participate in billing.

Only create draws that should be live after the cutoff date. Historical draws that have been closed or fully paid off don't need to be created as separate draws — their activity goes to the migration draw.


```json
POST /api/people/{personId}/loans/{loanId}/draws
Content-Type: application/json

{
  "externalId": "your-draw-id-001",
  "nickname": "Primary Draw",
  "status": "pending",
  "atOrigination": {
    "interestRates": [
      { "days": null, "rate": 0.1999 }
    ],
    "creditLimitAmount": 8000.00,
    "minPaymentCalculation": {
      "percentageOfPrincipal": 0.02,
      "minAmount": 25.00
    },
    "gracePeriod": {
      "enabled": true,
      "numDays": 25,
      "numPeriodsToRestoreGrace": 1
    }
  }
}
```

**Response** (abbreviated):


```json
{
  "data": {
    "id": "DR-0001-ABCD",
    "externalId": "your-draw-id-001",
    "status": "pending"
  }
}
```

Repeat for each active draw. Save each draw's `id` — you'll need them for migration period data and live activities.

### Upload loan documents

Upload loan agreements, historical statements, and any other relevant documents. Create a document descriptor first, then upload the file content.


```json
POST /api/people/{personId}/documents
Content-Type: application/json

{
  "type": "loanAgreement",
  "description": "Original LOC Agreement",
  "status": "accepted",
  "loanId": "LN-1234-ABCD",
  "sensitiveData": false
}
```


```
POST /api/people/{personId}/documents/{documentDescriptorId}/content
Content-Type: multipart/form-data

file=@/path/to/agreement.pdf
```

If you're providing historical statements for past periods (Step 3), save each document descriptor ID — you'll reference it when creating past period data.

## Step 3: Populate historical data

Provide Peach with the borrower's historical billing cycles and set up the migration period with current balances. This step has three parts: past periods, migration period LOC data, and migration period draw data.

### Create past periods data

Past periods are billing-cycle snapshots from your legacy system covering the borrower's history before the migration cutoff date. They appear in the borrower's statement history but are not replayed.

**Period date rules:**

- Periods must be contiguous — no gaps between them. The `startDate` of each period must equal the day after the previous period's `endDate`.
- The `statementDate` for each period must equal the day after the period's `endDate`.
- The `dueDate` must fall within the **following** period's date range.
- No two periods can have overlapping start-end date ranges.



```json
POST /api/people/{personId}/loans/{loanId}/migration/past-periods
Content-Type: application/json

[
  {
    "startDate": "2024-04-01",
    "endDate": "2024-04-30",
    "statementDate": "2024-05-01",
    "dueDate": "2024-05-22",
    "statement": {
      "documentDescriptorId": "DD-APR-ABCD",
      "creditBalanceAmount": 0.00,
      "minimumAmountDue": 125.00,
      "newBalanceAmount": 3450.75
    },
    "gracePeriod": {
      "fullBalanceAmount": 3450.75,
      "fullBalanceMinusOverdueAmount": 3450.75,
      "isGracePeriodEligible": true
    }
  },
  {
    "startDate": "2024-05-01",
    "endDate": "2024-05-31",
    "statementDate": "2024-06-01",
    "dueDate": "2024-06-22",
    "statement": {
      "documentDescriptorId": "DD-MAY-ABCD",
      "creditBalanceAmount": 0.00,
      "minimumAmountDue": 118.50,
      "newBalanceAmount": 3200.00
    },
    "gracePeriod": {
      "fullBalanceAmount": 3200.00,
      "fullBalanceMinusOverdueAmount": 3200.00,
      "isGracePeriodEligible": true
    }
  }
]
```

Include all historical periods from the loan's activation through the period immediately before the migration period. The `statement.documentDescriptorId` is optional — include it if you uploaded the corresponding statement document.

**Grace period fields in past periods:**

| Field | Description |
|  --- | --- |
| `fullBalanceAmount` | Total balance eligible for grace period evaluation |
| `fullBalanceMinusOverdueAmount` | Full balance minus any overdue amount |
| `totalFulfilledOnDueDateAmount` | Amount paid by the due date. Set this on the period one day after the due date. |
| `isGracePeriodEligible` | Whether the borrower maintained grace eligibility through this period |


### Create migration period LOC data

The migration period LOC data represents the line-level state as of the migration cutoff date. This is where you provide the balance snapshot and obligation information that Peach uses as its starting point.


```json
POST /api/people/{personId}/loans/{loanId}/migration/period
Content-Type: application/json

{
  "startDate": "2024-08-01",
  "endDate": "2024-08-31",
  "statementDate": "2024-09-01",
  "dueDate": "2024-09-22",
  "balances": {
    "nonDueBalances": {
      "nonDueOriginationFeesAmount": 0.00,
      "nonDueLateFeesAmount": 0.00
    },
    "dueBalances": {
      "dueOriginationFeesAmount": 0.00,
      "dueLateFeesAmount": 0.00
    },
    "overdueBalances": {
      "overdueOriginationFeesAmount": 0.00,
      "overdueLateFeesAmount": 0.00
    },
    "creditLimitAmount": 10000.00,
    "reimbursementAmount": 0.00
  },
  "obligation": {
    "obligationAmount": 125.00,
    "migratedDaysOverdue": 0,
    "migratedOverdueFromDate": null
  },
  "gracePeriod": {
    "isGracePeriodEligible": true,
    "fullBalanceAmount": 2850.00,
    "fullBalanceMinusOverdueAmount": 2850.00
  }
}
```

**Balance fields at the line level** include only origination fees and late fees broken down by non-due, due, and overdue. The line level can only carry fee balances — principal and interest balances are tracked at the draw level, not the line level. Do not post principal or interest balance totals on the line.

| Field | Description |
|  --- | --- |
| `creditLimitAmount` | Current overall credit limit for the line |
| `reimbursementAmount` | Amount the lender owes to the borrower (credit balances). Set to `0.00` if not applicable. |
| `obligation.obligationAmount` | Remaining amount the borrower must pay by the upcoming due date |
| `obligation.migratedDaysOverdue` | Number of days the account was overdue as of the cutoff date. Set to `0` if current. |
| `obligation.migratedOverdueFromDate` | Date from which the account has been overdue. Set to `null` if the account is current, **or** if the overdue date is the same as the migration cutoff date. For example, if you are migrating on 2/1, the migration cutoff is 2/1, and the loan is overdue 7 days, you can set `migratedOverdueFromDate` to `null` and `migratedDaysOverdue` to `7`. |


All balance amounts must be ≥ 0.00 with a precision of 0.01 (two decimal places).

### Create migration period draw data

For **each active draw** (not the migration draw), create migration period data with draw-level balances and obligations.


```json
POST /api/people/{personId}/loans/{loanId}/draws/{drawId}/migration/period
Content-Type: application/json

{
  "balances": {
    "nonDueBalances": {
      "nonDuePrincipalAmount": 2200.00,
      "nonDueInterestAmount": 0.00,
      "nonDueDrawFeesAmount": 0.00,
      "nonDueLateFeesAmount": 0.00,
      "nonDueModificationFeesAmount": 0.00
    },
    "dueBalances": {
      "duePrincipalAmount": 50.00,
      "dueInterestAmount": 37.50,
      "dueDrawFeesAmount": 0.00,
      "dueLateFeesAmount": 0.00,
      "dueModificationFeesAmount": 0.00
    },
    "overdueBalances": {
      "overduePrincipalAmount": 0.00,
      "overdueInterestAmount": 0.00,
      "overdueDrawFeesAmount": 0.00,
      "overdueLateFeesAmount": 0.00,
      "overdueModificationFeesAmount": 0.00
    },
    "creditLimitAmount": 8000.00
  },
  "obligation": {
    "obligationAmount": 87.50,
    "migratedDaysOverdue": 0
  },
  "gracePeriod": {
    "isGracePeriodEligible": true,
    "fullBalanceAmount": 2287.50,
    "fullBalanceMinusOverdueAmount": 2287.50
  }
}
```

Draw-level balances provide a more granular breakdown than line-level balances, including principal, interest, draw fees, late fees, and modification fees for each draw.

**Late fees vs. modification fees:** Peach maintains separate ledger buckets for late fees and modification fees. You must specify which fee type each balance belongs to — the system needs to know the correct ledger bucket for each amount. Ensure your `lateFeesAmount` and `modificationFeesAmount` fields accurately reflect the fee type from your legacy system.

Repeat this call for every active draw. Note that line-level and draw-level balances are independent — the line level only carries fee balances (origination fees, late fees), while draws carry principal, interest, and draw-specific fees. Do not attempt to reconcile draw-level totals against line-level totals, as there is validation that prevents the line from having principal or interest balances. Similarly, the sum of draw-level `creditLimitAmount` values should not exceed the line-level credit limit.

### Change loan status to Originated

After populating all historical data, change the loan status from `pending` to `originated`. This locks the `atOrigination` data.


```json
PUT /api/people/{personId}/loans/{loanId}
Content-Type: application/json

{
  "status": "originated"
}
```

Irreversible after origination
After changing status to `originated`, you cannot modify the `atOrigination` object. Make sure all loan terms, interest rates, and configuration are correct before this step.

## Step 4: Create activities

Create all purchases, transactions (payments), and fees — both historical and live. The distinction between historical and live determines which endpoint you use and which draw receives the activity.

### Historical purchases (before the migration cutoff date)

Post historical purchases to the **migration draw**, not to the actual draws. Use the `migration.originalDrawId` field to record which legacy draw the purchase originally belonged to. This enables proper balance adjustments if the purchase is later disputed.


```json
POST /api/people/{personId}/loans/{loanId}/draws/{migrationDrawId}/purchases
Content-Type: application/json

{
  "externalId": "your-purchase-id-001",
  "type": "regular",
  "status": "settled",
  "amount": 249.99,
  "purchaseDate": "2024-07-10",
  "purchaseDetails": {
    "description": "Electronics Store",
    "pointOfSaleType": "inStore",
    "merchantName": "Best Buy #1234",
    "merchantNumber": "MRC-9876"
  },
  "migration": {
    "originalDrawId": "your-legacy-draw-id-001"
  }
}
```

Purchases in `authorized` status are not allowed in past periods (before the migration cutoff date). All historical purchases must have a terminal status such as `settled`, `canceled`, or `returned`.

### Live purchases (on or after the migration cutoff date)

Post live purchases to the **actual draw** they belong to. Do not include the `migration` object.


```json
POST /api/people/{personId}/loans/{loanId}/draws/{drawId}/purchases
Content-Type: application/json

{
  "externalId": "your-purchase-id-042",
  "type": "regular",
  "status": "settled",
  "amount": 75.50,
  "purchaseDate": "2024-08-05",
  "purchaseDetails": {
    "description": "Grocery Store",
    "pointOfSaleType": "inStore",
    "merchantName": "Whole Foods #567"
  }
}
```

### Historical transactions (before the migration cutoff date)

Use the **migration-specific past-transaction endpoint** for payments that occurred before the cutoff date. Include `drawSplitDetails` to record how the payment was allocated across draws — this is essential for handling reversals or status changes after migration.


```json
POST /api/people/{personId}/loans/{loanId}/migration/past-transaction
Content-Type: application/json

{
  "externalId": "your-payment-id-001",
  "amount": 200.00,
  "type": "oneTimePayment",
  "status": "succeeded",
  "effectiveDate": "2024-07-22",
  "effectiveTimeOfDay": {
    "hour": 14,
    "minute": 30,
    "second": 0
  },
  "paymentInstrumentId": "PI-1234-ABCD",
  "migration": {
    "drawSplitDetails": [
      {
        "originalDrawId": "your-legacy-draw-id-001",
        "drawAllocatedAmount": 200.00
      }
    ]
  }
}
```

**Transaction types supported:**

- `oneTimePayment` — Standard payment
- `serviceCredit` — Credit applied to the account (note: you cannot pass `status` for service credits — the system automatically sets it to `succeeded`)
- `downPayment` — Down payment


**Status options** (for `oneTimePayment` and `downPayment`): `initiated`, `pending`, `succeeded`, `failed`, `canceled`, `inDispute`, `chargeback`

**`drawSplitDetails` are critical for post-migration modifications.** If you need to change a historical transaction's status after migration (e.g., from `succeeded` to `failed`), Peach uses the `drawSplitDetails` to determine which draw balances to adjust. If you omit `drawSplitDetails`, post-migration status changes on that transaction will fail.

**Important constraints:**

- Only reference **active draw IDs** in `drawSplitDetails` — do **not** use the migration draw ID.
- `drawSplitDetails` are only returned by the `GET .../migration/past-transaction` endpoint, not the standard `GET .../transactions` endpoint.


**Inline payment instrument details:** If you don't want to create a separate payment instrument record for a historical transaction, you can provide details inline:


```json
{
  "paymentInstrumentDetails": {
    "type": "bankAccount",
    "accountLastFour": "5678",
    "customDisplayName": "Legacy Checking"
  }
}
```

### Live transactions (on or after the migration cutoff date)

Use the **standard transaction endpoint** with `isExternal: true` for payments processed by your legacy system after the cutoff date.


```json
POST /api/people/{personId}/loans/{loanId}/transactions
Content-Type: application/json

{
  "externalId": "your-payment-id-042",
  "paymentInstrumentId": "PI-1234-ABCD",
  "amount": 150.00,
  "type": "oneTime",
  "status": "succeeded",
  "isExternal": true,
  "effectiveDate": "2024-08-15",
  "effectiveTimeOfDay": {
    "hour": 10,
    "minute": 0,
    "second": 0
  }
}
```

- The `type` field differs between endpoints: the past-transaction endpoint uses `oneTimePayment`, `serviceCredit`, and `downPayment`, while the standard transaction endpoint uses `oneTime`, `serviceCredit`, etc. Use the correct type value for the endpoint you're calling.
- The `effectiveTimeOfDay` must be after 2:00 AM in the product timezone. Transactions with earlier times will fail validation.


### Historical fees (before the migration cutoff date)

Create fees using the standard fee endpoint, but include `migration.originalDrawId` to associate the fee with the correct legacy draw. For purchase-related fees (e.g., foreign transaction fees), also include `migration.originalPurchaseId`.


```json
POST /api/people/{personId}/loans/{loanId}/fees
Content-Type: application/json

{
  "feeTypeId": "FT-LATE-ABCD",
  "amount": 29.00,
  "chargeDate": "2024-07-25",
  "chargeTimeOfDay": {
    "hour": 10,
    "minute": 0,
    "second": 0
  },
  "migration": {
    "originalDrawId": "your-legacy-draw-id-001"
  }
}
```

**Fee types with special migration fields:**

| Fee type | Additional migration field | Description |
|  --- | --- | --- |
| Late fees, annual fees | `migration.originalDrawId` | Associates with a legacy draw. Omit for line-level fees. |
| NSF fees | `migration.originalTransactionId` | Associates with the failed transaction |
| Foreign transaction fees | `migration.originalPurchaseId` | Associates with the purchase that triggered the fee |


### Live fees (on or after the migration cutoff date)

Create live fees normally, without the `migration` object.


```json
POST /api/people/{personId}/loans/{loanId}/fees
Content-Type: application/json

{
  "feeTypeId": "FT-LATE-ABCD",
  "amount": 29.00,
  "chargeDate": "2024-08-25",
  "chargeTimeOfDay": {
    "hour": 10,
    "minute": 0,
    "second": 0
  }
}
```

## Step 5: Execute migration

With all data in place, trigger the migration process. Peach validates the prepared data, replays live activities, and activates the loan.

### Call the migrate endpoint


```json
POST /api/people/{personId}/loans/{loanId}/migrate
Content-Type: application/json

{
  "sync": false
}
```

| Parameter | Description |
|  --- | --- |
| `sync: true` | Synchronous — the API call blocks until migration completes or fails. **Timeout: 60 seconds.** If migration takes longer, the call returns `408 Request Timeout` but migration continues in the background. Use for single-loan testing only. |
| `sync: false` | Asynchronous — the API call returns immediately and migration runs in the background. **Recommended for production and bulk migrations.** |


### Monitor migration status

For asynchronous migrations, poll the loan endpoint to check progress:


```json
GET /api/people/{personId}/loans/{loanId}
```

Watch the `migrationStatus` field:

| Status | Meaning |
|  --- | --- |
| `prepMigration` | Data preparation phase (pre-migrate call) |
| `migrating` | Migration in progress — Peach is replaying live events |
| `completed` | Migration completed successfully |
| `failed` | Migration failed — data has been rolled back |


### What happens during migration

When you call the migrate endpoint, Peach performs the following sequence:

1. **Validates** all migration data (periods, balances, transactions, draws).
2. **Locks** the loan to prevent concurrent modifications.
3. **Originates** the loan if it's in `pending` status.
4. **Sets** line-level grace period information from the migration period data.
5. **Activates** the LOC and updates draw information for the migration period.
6. **Generates** daily ledger management events.
7. **Replays** all live activities (purchases, transactions, fees) in chronological order.
8. **Sets** `migrationStatus` to `completed`.
9. **Fires** a `loan.migration.succeeded` webhook event.
10. **Fires** webhooks for all events created for live activities.


### If migration succeeds

- The loan status progresses from `pending` → `originated` → `active`.
- `migrationStatus` changes to `completed`.
- A `loan.migration.succeeded` event is created and a webhook fires.
- All live activities are replayed and reflected in the loan's balance.
- The loan begins accruing interest and generating billing events according to its configuration.


### If migration fails

- The system rolls back the ledger — no partial state is left.
- The loan status reverts to `pending`.
- `migrationStatus` changes to `failed`.
- A `loan.migration.failed` event is created and a webhook fires.


**To retry after a failure:**

1. Review the error details in the migration event.
2. Correct the issue (fix data, adjust balances, etc.).
3. Reset `migrationStatus` to `prepMigration`:



```json
PUT /api/people/{personId}/loans/{loanId}
Content-Type: application/json

{
  "migration": {
    "migrationStatus": "prepMigration"
  }
}
```

1. Make any necessary data corrections.
2. Call the migrate endpoint again.


If `migrationStatus` shows `failed` but you don't see specific errors, contact Peach Support for investigation.

## See also

- [After LOC migration](/loan-lifecycle/loc-migration/after-migration) — Validate balances, configure autopay and credit reporting, and post-migration behavior.
- [LOC migration validation and troubleshooting](/loan-lifecycle/loc-migration/validation-and-troubleshooting) — Validation rules and a troubleshooting FAQ.
- [LOC migration API and webhooks reference](/loan-lifecycle/loc-migration/api-and-webhooks-reference) — The endpoints used across this procedure.