LendEasy/DocsLMS + Servicing·v1
Start integrating
GuidesLoansMigrate your portfolio

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 original captureMethod and 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 the providerTokenRef from your vault provider—never a raw account number;
  • co-borrowers and authorized parties on each loan via POST /v1/loans/{loanId}/parties once 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,transactions returns the authoritative schedule and posted transaction history;
  • GET /v1/loans/{loanId}/summary returns 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.61
Result$7,975.61 principal remaining

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

Never put a government identifier or account number in 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}/summary against the source system’s closing extract—principal, overdue components, payoff, next due date. GET /v1/portfolio/search confirms 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 via POST /v1/documents and link them to the loan, so the full paper trail is readable where servicing happens.
  • Reconciliation. Boarded RECORDED_EXTERNAL fundings and payments flow through the same reconciliation that watches production money. Differences between recorded history and the resulting ledger surface as typed exceptions at GET /v1/recon-exceptions—work the queue to zero, exporting via GET /v1/recon-exceptions/export for working sessions, before declaring cutover complete.
  • Accounting. Every boarded posting produced balanced journal entries; GET /v1/journalentries filtered 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.
Boarding and production use identical paths and payload shapes in Sandbox and Production. Rehearse the full sequence in Sandbox with a portfolio extract first; the run that boards production should be a replay of a rehearsal that already reconciled to the cent. Operation-level status for every route above is on API availability.
Unified search across guides, recipes & the API referenceEsc