Migrate your portfolio
Board an established portfolio onto the LendEasy Lending Core with the same public operations production uses—real terms, historical money recorded as external, and a ledger verified to the cent before cutover.
Migration moves your loans onto the LendEasy Lending Core—the native system of record—so servicing, evidence, and money movement run on one authoritative ledger from day one. There is no separate import dialect to learn and later abandon: boarding uses the public operations your integration runs in production, every mutation accepts an Idempotency-Key so an interrupted batch resumes without double-posting, and every boarded fact lands in the same read models, history ledgers, and reconciliation queues as a natively originated loan. Established lenders who prefer to keep an existing core can adopt the servicing plane through the additional bring-your-own-core deployment option; migration is how the portfolio itself comes home.
The walkthrough below re-tells Harbor—Maya Chen’s $8,400 loan—as if it had begun life on your previous system: same terms, same dates, same numbers, boarded instead of originated fresh.
The boarding sequence
Board in dependency order. Each stage produces the identifiers the next stage needs, and nothing accrues or moves until the historical disbursement posts.
1. Board the party graph
Create each customer aggregate with its current addresses and contact points in one request to POST /v1/customers, carrying your source key in externalId so every later record correlates back to the old system. Then attach the rest through the focused child routes:
- government identifiers via
POST /v1/customers/{customerId}/identifiers—supply the complete value once; ordinary reads return only the normalized mask; - consent records via
POST /v1/customers/{customerId}/consents, each with its originalcaptureMethodand evidence reference—a stored phone number is not texting permission, so carry the consent facts explicitly rather than assuming them; - payment instruments via
POST /v1/customers/{customerId}/payment-instruments, passing theproviderTokenReffrom your vault provider—never a raw account number; - co-borrowers and authorized parties on each loan via
POST /v1/loans/{loanId}/partiesonce loans exist.
Boarded contact points, identifiers, and instruments start UNVERIFIED. Verification is written by trusted integrations and provider events, not asserted by the migration—an instrument must reach ACTIVE and VERIFIED before it can move money, so schedule instrument verification early if borrowers pay by ACH on day one.
2. Map your products
Recreate each product at POST /v1/loanproducts with the native calculation settings that reproduce your source system’s math—interest type, calculation period, amortization, and the transaction processing strategy—plus the lmsConfig disclosure and statement policy. Charge definitions board first at POST /v1/charges so products can attach them by id. Because the terms a loan resolves at origination are frozen on the loan, mapping is a one-time exercise per product, not a per-loan negotiation; see Products.
3. Originate each loan with its real terms
Before persisting anything, send the boarded terms to POST /v1/loans?command=calculateLoanSchedule. The preview returns the full amortization schedule without creating a loan—diff it against the source system’s schedule and resolve any product-mapping difference now, while it is still free to fix.
Then create and approve the loan with its historical dates:
curl -X POST "$BASE/v1/loans" \
-H "Authorization: Bearer $TOKEN" \
-H "LendEasy-Tenant: demo-lender" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: board-harbor-loan-01" \
-d '{
"clientId": 412,
"productId": 3,
"loanType": "individual",
"principal": 8400.0,
"loanTermFrequency": 18,
"loanTermFrequencyType": 2,
"numberOfRepayments": 18,
"repaymentEvery": 1,
"repaymentFrequencyType": 2,
"interestRatePerPeriod": 13.25,
"interestType": 0,
"interestCalculationPeriodType": 1,
"amortizationType": 1,
"transactionProcessingStrategyCode": "advanced-payment-allocation-strategy",
"expectedDisbursementDate": "2026-08-12",
"submittedOnDate": "2026-08-11",
"externalId": "origination-HBR-2026-001",
"dateFormat": "yyyy-MM-dd",
"locale": "en",
"charges": [{ "chargeId": 12, "amount": 252.0 }]
}'
POST /v1/loans/7204?command=approve carries the original approvedOnDate and approvedLoanAmount. Approval computes and finalizes the APR disclosure from the boarded terms—if the disclosure cannot be produced, the approval fails, which surfaces a terms-mapping error before any money is recorded rather than after cutover.
4. Record the historical funding as external
The original disbursement already happened on your previous system, so no payout travels a rail. Record it:
curl -X POST "$BASE/v1/loans/7204/fundings/record-external" \
-H "Authorization: Bearer $TOKEN" \
-H "LendEasy-Tenant: demo-lender" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: board-harbor-funding-01" \
-d '{
"amount": "8400.00",
"currency": "USD",
"fundingType": "DISBURSEMENT",
"recipientType": "BORROWER",
"recipientRef": "412",
"externalRecordRef": "legacy-disb-HBR-2026-001",
"reason": "Boarded disbursement funded by the previous system on 2026-08-12."
}'
The funding enters as RECORDED_EXTERNAL and flows into reconciliation, where it is confirmed against your source ledger. The disbursement posts value-dated to the original funding date—Harbor activates as of August 12, 2026, the day the money actually left the previous lender’s control, so accrual, the live schedule, and due-date statements all run from the historical date rather than migration day. A disbursement recording still requires an approved loan, may not exceed the approved principal, and is blocked by an active DISBURSEMENT_HOLD.
5. Replay the payment history
Record each historical payment in chronological order with POST /v1/loans/{loanId}/payments/record-external—the documented path for money collected outside LendEasy. Each record carries the amount, the original effectiveDate, a unique externalRecordRef (your source transaction ID, which makes the replay idempotent by construction), source: "IMPORT", and a rationale:
curl -X POST "$BASE/v1/loans/7204/payments/record-external" \
-H "Authorization: Bearer $TOKEN" \
-H "LendEasy-Tenant: demo-lender" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: board-harbor-payment-0001" \
-d '{
"amount": "517.14",
"currency": "USD",
"effectiveDate": "2026-09-12",
"externalRecordRef": "legacy-txn-HBR-0001",
"source": "IMPORT",
"rationale": "Boarded installment 1 of 18 collected by the previous servicer."
}'
LendEasy moves no money. Posting, allocation, and receipts work exactly as they do for rail payments: the authoritative ledger performs the split, value-dated to the effective date, and records the transaction reference. Replaying history in order therefore reproduces your allocation results installment by installment instead of trusting a summarized balance.
6. Verify the schedule against the source system
After each loan’s history posts, read it back and diff:
GET /v1/loans/{loanId}?associations=repaymentSchedule,transactionsreturns the authoritative schedule and posted transaction history;GET /v1/loans/{loanId}/summaryreturns balances, overdue components, payoff projection, the APR disclosure, and active restrictions in one call.
Harbor after replaying installment 1
opening principal $8,400.00
periodic interest $92.75
posted payment $517.14
principal allocation $424.39
closing principal $7,975.61If the closing principal disagrees with the source system, the difference is in the boarded facts—terms, a charge, or a missing transaction—and the fix is a corrected record, never a manual balance adjustment.
Mid-life boarding realities
Delinquent loans board by telling the truth. Record exactly the payments that were made; the installments that were missed simply remain uncovered, and delinquency is derived from posted history rather than imported as a flag. Cedar’s two overdue $330 installments arrive as the same $660 past due on LendEasy because the posted record shows it, and collections facts, fees, and eligibility follow from there.
Restructured loans replay their restructuring. Where the previous system re-aged, re-amortized, or rescheduled a loan, apply the same change through the governed commands—re-age, re-amortize, reschedule—after the payment history that preceded it. The boarded loan then carries the modification and its evidence trail on LendEasy, not just its arithmetic result.
In-flight payments straddle the cutover. A debit initiated on the old system that settles after cutover is still external money: record it with record-external once it settles, value-dated to its original effective date. Do not create a rail intent for money already moving elsewhere—one authorization, one record. Freeze new payment initiation on the source system before you replay its final transactions, so the boarded history has a clean end.
Restrictions come first, automation second. Re-apply every active hold—PAYMENT_HOLD, COLLECTIONS_HOLD, COMMUNICATION_HOLD, SCRA_MLA_PROTECTED—via POST /v1/customers/{customerId}/restrictions before any payment, autopay, or outreach automation touches the boarded book. The server derives the effective time on apply; the historical interval stays in your source records, referenced through the restriction’s source and case evidence. See Apply a servicing restriction.
Autopay re-enrolls; it does not import. Once a boarded instrument is ACTIVE and VERIFIED, enroll autopay carrying the recurring authorization evidence in authorizationRef. Enrollment reads the current due obligation and creates normal payment intents—it never backfills history.
externalId, an externalRecordRef, a rationale, an idempotency key, or a note during boarding. Those values are operationally visible by design; complete values belong only on the identifier and instrument routes built to protect them.Cutover verification
Before pointing borrowers and agents at LendEasy, prove the board rather than sampling it:
- Read models. Diff every loan’s
GET /v1/loans/{loanId}/summaryagainst the source system’s closing extract—principal, overdue components, payoff, next due date.GET /v1/portfolio/searchconfirms boarded customers resolve by name, email, or phone. - Statements. Due-date statements are scheduled from activation, so the first LendEasy statement covers the first cycle after cutover; list them at
GET /v1/loans/{loanId}/statements. Board historical statements and signed agreements as documents viaPOST /v1/documentsand link them to the loan, so the full paper trail is readable where servicing happens. - Reconciliation. Boarded
RECORDED_EXTERNALfundings and payments flow through the same reconciliation that watches production money. Differences between recorded history and the resulting ledger surface as typed exceptions atGET /v1/recon-exceptions—work the queue to zero, exporting viaGET /v1/recon-exceptions/exportfor working sessions, before declaring cutover complete. - Accounting. Every boarded posting produced balanced journal entries;
GET /v1/journalentriesfiltered by loan lets your accountants tie the migrated receivable to the general ledger. See Accounting. - History. The customer history ledger and per-loan governance records show who boarded what, when, and under which command—your migration audit trail is the platform’s ordinary audit trail.