{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":[]},"type":"markdown"},"seo":{"title":"LOC migration validation and troubleshooting","siteUrl":"https://docs.peachfinance.com","description":"API and product documentation for Peach Finance, a lending-as-a-service platform.","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"loc-migration-validation-and-troubleshooting","__idx":0},"children":["LOC migration validation and troubleshooting"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"validation-rules-and-common-errors","__idx":1},"children":["Validation rules and common errors"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Peach validates migration data at multiple points. Understanding these rules helps prevent failures."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"period-validation","__idx":2},"children":["Period validation"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"align":"left","data-label":"Rule"},"children":["Rule"]},{"$$mdtype":"Tag","name":"th","attributes":{"align":"left","data-label":"Error symptom"},"children":["Error symptom"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["No gaps between periods"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["startDate"]}," of period N+1 must equal ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["endDate"]}," of period N + 1 day"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["No overlapping start-end date ranges"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["startDate"]},"–",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["endDate"]}," range of one period cannot overlap with another period's range"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["No duplicate dates"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Two periods cannot share the same ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["startDate"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["endDate"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["statementDate"]},", or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dueDate"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Due date falls in next period"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Each period's ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["dueDate"]}," must fall between the next period's ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["startDate"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["endDate"]}]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"transaction-validation","__idx":3},"children":["Transaction validation"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"align":"left","data-label":"Rule"},"children":["Rule"]},{"$$mdtype":"Tag","name":"th","attributes":{"align":"left","data-label":"Error symptom"},"children":["Error symptom"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["effectiveTimeOfDay"]}," must be after 2:00 AM"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Transactions with earlier times fail validation"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["External transactions require ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["isExternal: true"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Live transactions processed by legacy system must be flagged as external"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Historical transactions use the past-transaction endpoint"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Don't use the standard transaction endpoint for pre-cutoff activity"]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"balance-validation","__idx":4},"children":["Balance validation"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"align":"left","data-label":"Rule"},"children":["Rule"]},{"$$mdtype":"Tag","name":"th","attributes":{"align":"left","data-label":"Error symptom"},"children":["Error symptom"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["All amounts ≥ 0.00"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Negative amounts are rejected"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Precision of 0.01"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Amounts with more than 2 decimal places are rejected"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Credit balances use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["reimbursementAmount"]}]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Don't use negative principal for credit balances"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Overdue balances must match obligations"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["If the obligation states the borrower is overdue by a certain amount, the overdue balance amounts across draws must add up to that amount. Mismatches will cause migration to fail."]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"troubleshooting-and-faq","__idx":5},"children":["Troubleshooting and FAQ"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"balance-discrepancies","__idx":6},"children":["Balance discrepancies"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Q: Why doesn't my balance match the legacy system?"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Balance discrepancies after migration almost always trace back to one of the following root causes. Work through this diagnostic sequence:"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Step 1 — Compare total balance first."]}," If the total outstanding balance (principal + interest + fees) matches but the breakdown doesn't, skip to \"Principal/interest split differences\" below. If the total doesn't match, continue."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Step 2 — Check balance timing."]}," Balances must be provided as of the migration cutoff date. The codebase enforces a specific rule: balances should include interest accrued through the last day of the previous period (endDate) but should NOT include interest for the statement date (migration period start date). If you included an extra day of interest, you'll see a small positive discrepancy."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Step 3 — Check day count conventions."]}," Peach accrues interest for the actual number of days in each month. Some legacy systems use a 30-day month convention (30/360). This means:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"align":"left","data-label":"Month"},"children":["Month"]},{"$$mdtype":"Tag","name":"th","attributes":{"align":"left","data-label":"Peach (actual days)"},"children":["Peach (actual days)"]},{"$$mdtype":"Tag","name":"th","attributes":{"align":"left","data-label":"30/360 system"},"children":["30/360 system"]},{"$$mdtype":"Tag","name":"th","attributes":{"align":"left","data-label":"Difference per $10,000 at 18% APR"},"children":["Difference per $10,000 at 18% APR"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["February (non-leap)"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["28 days"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["30 days"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["-$0.99"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["February (leap)"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["29 days"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["30 days"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["-$0.49"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["March"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["31 days"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["30 days"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["+$0.49"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Over a full year these differences partially cancel out, but at any given cutoff date there may be a discrepancy of a few dollars."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Step 4 — Check rounding behavior."]}," Peach accrues interest daily with high precision, then truncates to the nearest cent at statement date. Fractional cents go to a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["forgone_interest_rounding"]}," bucket. If your legacy system rounds at a different cadence (e.g., per-period rather than per-day), small cumulative rounding differences are expected."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Step 5 — Break down by draw."]}," Compare balances at the draw level. If one specific draw is off, check that draw's interest rate, promo rate, and fee configuration. A mismatched rate will compound over the post-cutoff period."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Minor discrepancies (typically < $1.00) are normal and can be addressed with service credits or adjustment fees. See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/loan-lifecycle/loc-migration/after-migration#adjust-balances"},"children":["Step 6: Post-migration → Adjust balances"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"principalinterest-split-differences","__idx":7},"children":["Principal/interest split differences"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Q: Why is the principal/interest split different from my legacy system?"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Peach replays live activity chronologically and applies its own interest calculation rules. Common causes of split differences:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Accrual method"]},": Peach uses daily accrual with actual/365 day counting. If your legacy system uses 30/360 or periodic (monthly) accrual, the interest component will differ."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Interest truncation at statement date"]},": For LOCs, when non-due interest moves to due at statement date, the system truncates to the nearest cent. The fractional amount goes to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["forgone_interest_rounding"]},". If your legacy system doesn't truncate at this step, interest will accumulate differently."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Grace period retroactive accrual"]},": If a borrower is in grace and makes a payment between statement date and due date, Peach backdates the payment's ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["effectiveDate"]}," to the statement date for interest calculation purposes. If your legacy system applies the payment at the actual date, the interest calculation will differ for that period."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The total balance should still match within tolerance — if it doesn't, check the balance timing and day count rules above."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"interest-calculation-after-grace-period-revocation","__idx":8},"children":["Interest calculation after grace period revocation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Q: Why did the borrower get a large interest charge after migration?"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If the borrower was in grace before migration but the grace period was revoked post-migration (because they didn't pay in full by the due date), Peach accrues interest retroactively on the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["unpaid portion"]}," from the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["statement date"]},", not from the due date. This is by design (see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/loan-lifecycle/loc-migration/how-grace-periods-work"},"children":["Appendix B: Grace Period for Lines of Credit"]},") but can produce a larger interest charge than expected if the borrower had been in grace for a long period."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Check: Was ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["isGracePeriodEligible"]}," set correctly in the migration period data? If it was set to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["true"]}," but the borrower shouldn't have been in grace, the system will have suppressed interest accrual during the migration period and then retroactively accrued when grace was revoked — creating a sudden lump-sum charge."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"loan-tapes-after-migration","__idx":9},"children":["Loan tapes after migration"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Q: How do migrated loans appear on loan tapes?"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Peach does not generate loan tapes for periods before migration. After migration, the system generates loan tapes based on your tape configuration. Migrated loans appear on tapes as if they are any other loan. Canceled loans do not appear on loan tapes. Loan tape fields are conditionally enabled based on whether the loan has a migration cutoff — some fields that depend on full loan history may not be available for migrated loans."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"migration-processing-time","__idx":10},"children":["Migration processing time"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Q: How long does migration take?"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Processing time depends on the number of draws, historical activities (purchases, transactions, fees), and batch size."]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"align":"left","data-label":"Mode"},"children":["Mode"]},{"$$mdtype":"Tag","name":"th","attributes":{"align":"left","data-label":"Behavior"},"children":["Behavior"]},{"$$mdtype":"Tag","name":"th","attributes":{"align":"left","data-label":"Recommended for"},"children":["Recommended for"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Synchronous"]}," (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sync=true"]},")"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Blocks until migration completes or 60-second timeout"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Single loans, testing"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Asynchronous"]}," (default)"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Returns immediately, processes in background"]},{"$$mdtype":"Tag","name":"td","attributes":{"align":"left"},"children":["Production, batches"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If sync mode times out (HTTP 408), the migration is still processing — check ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["migrationStatus"]}," via ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GET /loans/{loanId}"]},". A single loan with a few draws and modest history typically migrates in seconds. Loans with many draws and extensive transaction history may take minutes."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For large batches, always use async mode. Monitor progress by polling loan status or subscribing to the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["loan.migration.succeeded"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["loan.migration.failed"]}," webhook events."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"failed-migration-with-no-errors","__idx":11},"children":["Failed migration with no errors"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Q: Migration status shows ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["failed"]}," but I don't see specific errors. What do I do?"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Contact Peach Support. Some failure scenarios (e.g., internal timeouts during replay) don't surface user-facing errors. Support can investigate the migration event logs."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When migration fails, the system automatically:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Rolls back the loan status to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pending"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Clears the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["migrated_at"]}," timestamp"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Reverses all ledger entries created during the migration attempt"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Resets the migration status to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["failed"]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can then reset to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["prepMigration"]},", correct any data issues, and attempt migration again."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"re-using-external-ids-after-cancellation","__idx":12},"children":["Re-using external IDs after cancellation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Q: I canceled a migrated loan but can't create a new loan with the same external ID."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["External IDs must be unique across all loans in your company, including canceled ones. Use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["clearAllExternalIds=true"]}," when canceling (see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/loan-lifecycle/loc-migration/strategy-and-operations#canceling-and-re-migrating-loans"},"children":["Canceling and re-migrating loans"]},"), or change the external ID on the incorrectly migrated loan before canceling (e.g., append ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["-canceled"]},")."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"sandbox-testing","__idx":13},"children":["Sandbox testing"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Q: Can I test the full migration workflow in sandbox?"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Yes. Sandbox supports the complete migration workflow. Limit testing to 5 loans at a time — larger batches may cause processing delays or aborted migrations."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"migration-validation-errors","__idx":14},"children":["Migration validation errors"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Q: What does \"Loan is missing ledger update event\" mean?"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST /migrate"]}," endpoint validates that every non-static draw has a corresponding ledger update event (created when you set up the migration period data). This error means you created a draw but didn't create migration period data for it. Create the migration period draw data (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST .../draws/{drawId}/migration/period"]},") for the missing draw, then retry migration."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Q: What does \"Operation would lead to periods overlapping\" mean?"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The period validation checks that no two periods have overlapping start-end date ranges, that there are no gaps between consecutive periods, and that ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["statementDate = endDate + 1 day"]},". Review your past periods data and migration period data for date inconsistencies. Common causes: daylight saving time boundaries, months with different day counts, or off-by-one errors in date calculations."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"see-also","__idx":15},"children":["See also"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/loan-lifecycle/loc-migration/migration-procedure"},"children":["LOC migration procedure"]}," — The steps that produce the data validated here."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/loan-lifecycle/loc-migration/after-migration"},"children":["After LOC migration"]}," — Validate balances and adjust discrepancies."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/loan-lifecycle/loc-migration/api-and-webhooks-reference"},"children":["LOC migration API and webhooks reference"]}," — Endpoints referenced in these errors."]}]}]},"headings":[{"value":"LOC migration validation and troubleshooting","id":"loc-migration-validation-and-troubleshooting","depth":1},{"value":"Validation rules and common errors","id":"validation-rules-and-common-errors","depth":2},{"value":"Period validation","id":"period-validation","depth":3},{"value":"Transaction validation","id":"transaction-validation","depth":3},{"value":"Balance validation","id":"balance-validation","depth":3},{"value":"Troubleshooting and FAQ","id":"troubleshooting-and-faq","depth":2},{"value":"Balance discrepancies","id":"balance-discrepancies","depth":3},{"value":"Principal/interest split differences","id":"principalinterest-split-differences","depth":3},{"value":"Interest calculation after grace period revocation","id":"interest-calculation-after-grace-period-revocation","depth":3},{"value":"Loan tapes after migration","id":"loan-tapes-after-migration","depth":3},{"value":"Migration processing time","id":"migration-processing-time","depth":3},{"value":"Failed migration with no errors","id":"failed-migration-with-no-errors","depth":3},{"value":"Re-using external IDs after cancellation","id":"re-using-external-ids-after-cancellation","depth":3},{"value":"Sandbox testing","id":"sandbox-testing","depth":3},{"value":"Migration validation errors","id":"migration-validation-errors","depth":3},{"value":"See also","id":"see-also","depth":2}],"frontmatter":{"title":"LOC migration validation and troubleshooting","description":"Validation rules Peach enforces during LOC migration plus a troubleshooting FAQ for balance discrepancies, failed migrations, and errors.","verifiedDate":"2026-06-10T00:00:00.000Z","verifiedSpec":"loc-migration.yml","verifiedRef":"peach@41d1f8a","seo":{"title":"LOC migration validation and troubleshooting"}},"lastModified":"2026-06-10T22:51:20.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/loan-lifecycle/loc-migration/validation-and-troubleshooting","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}