# Create a Loan Product (V2)

`POST /lokta-lms/api/v2/loanproducts`

Creates a Loan Product through the V2 contract.

Functionally equivalent to the V1 create operation — same parameters, validation, defaults, persistence and response — with one deliberate difference: loanScheduleType (CUMULATIVE, PROGRESSIVE or CONTRACTUAL) is mandatory and must be supplied explicitly.

Supports both Cumulative and Progressive loan products; the API version does not determine the schedule type.

Temporary integration tolerance: unknown JSON request-body fields are ignored — they are not validated, persisted, returned or audited, and must not be relied upon. Known fields remain strictly validated. A future release may restore unsupported-parameter rejection.

## Request body (application/json)
- `loanScheduleType` (string, required)
- `accountMovesOutOfNPAOnlyOnArrearsCompletion` (boolean)
- `accountingRule` (integer)
- `allowApprovedDisbursedAmountsOverApplied` (boolean)
- `allowAttributeOverrides` (AllowAttributeOverrides)
- `allowCompoundingOnEod` (boolean)
- `allowFullTermForTranche` (boolean): Allow full term length for each tranche disbursement. Only available for PROGRESSIVE schedule type with multi-disbursement enabled.
- `allowPartialPeriodInterestCalculation` (boolean)
- `allowVariableInstallments` (boolean)
- `amortizationType` (integer)
- `buyDownExpenseAccountId` (integer)
- `buyDownFeeCalculationType` (string)
- `buyDownFeeIncomeType` (string)
- `buyDownFeeStrategy` (string)
- `buydownfeeClassificationToIncomeAccountMappings` (array of PostClassificationToIncomeAccountMappings)
- `canDefineInstallmentAmount` (boolean)
- `canUseForTopup` (boolean)
- `capitalizedIncomeCalculationType` (string)
- `capitalizedIncomeClassificationToIncomeAccountMappings` (array of PostClassificationToIncomeAccountMappings)
- `capitalizedIncomeStrategy` (string)
- `capitalizedIncomeType` (string)
- `chargeOffBehaviour` (string)
- `chargeOffExpenseAccountId` (integer)
- `chargeOffFraudExpenseAccountId` (integer)
- `chargeOffReasonToExpenseAccountMappings` (array of PostChargeOffReasonToExpenseAccountMappings)
- `charges` (array of LoanProductV2ChargeEntry): Charge entries — existing charge ids plus optional per-association flags.
- `closeDate` (string)
- `creditAllocation` (array of CreditAllocationData)
- `currencyCode` (string)
- `dateFormat` (string)
- `daysInMonthType` (integer)
- `daysInYearCustomStrategy` (string)
- `daysInYearType` (integer)
- `deferredFeeIncomeAccountId` (integer)
- `deferredIncomeLiabilityAccountId` (integer)
- `delinquencyBucketId` (integer)
- `description` (string)
- `digitsAfterDecimal` (integer)
- `disallowExpectedDisbursements` (boolean)
- `disallowInterestCalculationOnPastDue` (boolean)
- `disbursedAmountPercentageForDownPayment` (number)
- `dueDaysForRepaymentEvent` (integer)
- `enableAccrualActivityPosting` (boolean)
- `enableAutoRepaymentForDownPayment` (boolean)
- `enableBuyDownFee` (boolean)
- `enableDownPayment` (boolean)
- `enableIncomeCapitalization` (boolean)
- `enableInstallmentLevelDelinquency` (boolean)
- `externalId` (string)
- `feeToIncomeAccountMappings` (array of LoanProductChargeToGLAccountMapper)
- `fixedLength` (integer)
- `fixedPrincipalPercentagePerInstallment` (number)
- `fundId` (integer)
- `fundSourceAccountId` (integer)
- `goodwillCreditAccountId` (integer)
- `graceOnArrearsAgeing` (integer)
- `graceOnInterestPayment` (integer)
- `graceOnPrincipalPayment` (integer)
- `holdGuaranteeFunds` (boolean)
- `inArrearsTolerance` (integer)
- `inMultiplesOf` (integer)
- `includeInBorrowerCycle` (boolean)
- `incomeFromBuyDownAccountId` (integer)
- `incomeFromCapitalizationAccountId` (integer)
- `incomeFromChargeOffFeesAccountId` (integer)
- `incomeFromChargeOffInterestAccountId` (integer)
- `incomeFromChargeOffPenaltyAccountId` (integer)
- `incomeFromFeeAccountId` (integer)
- `incomeFromGoodwillCreditFeesAccountId` (integer)
- `incomeFromGoodwillCreditInterestAccountId` (integer)
- `incomeFromGoodwillCreditPenaltyAccountId` (integer)
- `incomeFromPenaltyAccountId` (integer)
- `incomeFromRecoveryAccountId` (integer)
- `installmentAmountInMultiplesOf` (integer)
- `interestCalculationPeriodType` (integer)
- `interestOnLoanAccountId` (integer)
- `interestRateFrequencyType` (integer)
- `interestRatePerPeriod` (number)
- `interestRateVariationsForBorrowerCycle` (array)
- `interestRecalculationCompoundingMethod` (integer)
- `interestRecognitionOnDisbursementDate` (boolean)
- `interestType` (integer)
- `isArrearsBasedOnOriginalSchedule` (boolean)
- `isCompoundingToBePostedAsTransaction` (boolean)
- `isEqualAmortization` (boolean)
- `isInterestRecalculationEnabled` (boolean)
- `isLinkedToFloatingInterestRates` (boolean)
- `loanPortfolioAccountId` (integer)
- `loanScheduleProcessingType` (string)
- `locale` (string)
- `maxInterestRatePerPeriod` (number)
- `maxNumberOfRepayments` (integer)
- `maxPrincipal` (number)
- `maxTrancheCount` (integer)
- `merchantBuyDownFee` (boolean)
- `minInterestRatePerPeriod` (number)
- `minNumberOfRepayments` (integer)
- `minPrincipal` (number)
- `minimumDaysBetweenDisbursalAndFirstRepayment` (integer)
- `multiDisburseLoan` (boolean)
- `name` (string)
- `numberOfRepaymentVariationsForBorrowerCycle` (array)
- `numberOfRepayments` (integer)
- `outstandingLoanBalance` (number)
- `overAppliedCalculationType` (string)
- `overAppliedNumber` (integer)
- `overDueDaysForRepaymentEvent` (integer)
- `overdueDaysForNPA` (integer)
- `overpaymentLiabilityAccountId` (integer)
- `paymentAllocation` (array of AdvancedPaymentData)
- `paymentChannelToFundSourceMappings` (array of GetLoanPaymentChannelToFundSourceMappings)
- `penaltyToIncomeAccountMappings` (array of LoanProductChargeToGLAccountMapper)
- `preClosureInterestCalculationStrategy` (integer)
- `principal` (number)
- `principalThresholdForLastInstallment` (integer)
- `principalVariationsForBorrowerCycle` (array)
- `rates` (array of RateData)
- `recalculationCompoundingFrequencyInterval` (integer)
- `recalculationCompoundingFrequencyOnDayType` (integer)
- `recalculationCompoundingFrequencyType` (integer)
- `recalculationRestFrequencyInterval` (integer)
- `recalculationRestFrequencyType` (integer)
- `receivableFeeAccountId` (integer)
- `receivableInterestAccountId` (integer)
- `receivablePenaltyAccountId` (integer)
- `repaymentEvery` (integer)
- `repaymentFrequencyType` (integer)
- `repaymentStartDateType` (integer)
- `rescheduleStrategyMethod` (integer)
- `shortName` (string)
- `startDate` (string)
- `supportedInterestRefundTypes` (array)
- `transactionProcessingStrategyCode` (string)
- `transfersInSuspenseAccountId` (integer)
- `useBorrowerCycle` (boolean)
- `writeOffAccountId` (integer)
- `writeOffReasonsToExpenseMappings` (array of PostWriteOffReasonToExpenseAccountMappings)
- `beneficiaryType` (string): System-defined disbursement payee category. Optional; product-type applicability is a client-side concern until the parameter-metadata framework serves it.
- `bpiMethod` (string): how the stub between disbursement and the first instalment is billed. Priced by the CONTRACTUAL schedule type; refused for the others.
- `brokenPeriodDayCount` (string): day-count convention for broken (stub) periods — same choices as dayCountConvention (parity). Null = regular-period behaviour. Declarative product configuration: stored as sent; the broken-period interest engine consumes it in a later phase.
- `coLendingEligible` (boolean): Whether the product may participate in a co-lending arrangement. Optional; defaults to false.
- `collectBpiAtDisbursement` (boolean): whether broken-period interest is collected at disbursement. Refused for a schedule type that prices no broken period. Not yet consumed: no transaction or journal entry nets it from the proceeds (mock-up term preEmi). Defaults to false; explicit null is rejected. Declarative: stored as sent; the BPI engine consumes it in a later phase.
- `computeAprForKfs` (boolean): whether APR should be computed for KFS disclosures. Optional; defaults to false. Declarative: stored as sent; the APR/KFS engine consumes it in a later phase.
- `createAsDraft` (boolean): Create the product as a DRAFT: it cannot originate loans whatever its start and close dates say, and appears in no product selection list, until POST /v2/loanproducts/{productId}/activate releases it. Optional; defaults to false, in which case the product has no activation lifecycle at all and behaves exactly as it does today. TRANSITIONAL — this exists so the activation flow can be exercised before clients integrate the activate call; drafting becomes the V2 default once they do.
- `dayCountConvention` (string): stable day-count convention name — the V2-only ALTERNATIVE way to fulfil the mandatory daysInMonthType/daysInYearType axes (the backend derives and persists both). Supplying the raw axes alone remains valid (convention stays null); supplying both requires consistency with the derivation (ACT_365→1/365, ACT_360→1/360, ACT_ACT→1/1, D30_360_US and D30E_360→30/360 — stored distinctly; the engine-level US-vs-30E distinction is deferred).
- `defaultRoundingMode` (string): product-level java.math.RoundingMode NAME (never an ordinal or any internal identifier). Null inherits tenant rounding. Declarative: persisted via the shared rounding profile; engine wiring consumes it in a later phase.
- `interestRateStep` (number): fixed nominal rate increment in percentage points per annum. Fixed-rate products only — must be null/omitted for floating products. Null = continuous.
- `maximumDaysBetweenDisbursalAndFirstRepayment` (integer): maximum calendar days between disbursement and the first repayment — the ceiling sibling of minimumDaysBetweenDisbursalAndFirstRepayment (must be >= the floor). ACTIVELY enforced at loan origination for both schedule types. Null/omitted = no ceiling.
- `pmtType` (string): payment-formula variant. Optional; defaults to STANDARD_PMT (current engine behaviour). PRECISE_PMT is stored as declarative configuration; the precise-PMT engine consumes it in a later phase. Explicit null is rejected — send STANDARD_PMT to reset.
- `principalStep` (number): principal increment anchored at minimumPrincipal. Null/omitted = continuous band. Requires the complete principal band when supplied; the default principal must sit on the grid.
- `productCategoryTags` (array): Stable category-tag codes from code group LoanProductCategory. Optional; duplicates rejected.
- `productType` (string): Stable product-type code from code group LoanProductType (see productTypeOptions in the template). Optional.
- `repayOnDay` (array): pinned monthly repayment day(s), 1-31, unique — monthly-frequency products only. The ONLY external recurrence representation (the internal rule string is never exposed). Exactly ONE day is active: derived first repayment dates snap forward to it and supplied dates must fall on it (days 29-31 follow the existing short-month clamping). More than one day is rejected until the semi-monthly engine is delivered. Null clears; returned sorted ascending.
- `residualEnabled` (boolean): residual (balloon) schedule eligibility flag. Declarative: stored as sent; the residual schedule engine consumes it in a later phase.
- `scheduleSolver` (string): declarative input-mode selection for a configurator/LOS (D-27). Optional and NULLABLE — omitted or null means "not configured"; there is no server default. All five values are storable and none changes backend behaviour. The backend never derives Loan terms from scheduleSolver: Loan Product and Loan APIs always receive fully determined Principal, tenor and interest-rate values regardless of the configured solver.
- `tenorStep` (integer): increment in NUMBER OF INSTALLMENTS over the repayment-count band (never a calendar duration). Null/omitted = continuous.

## Responses
- `200`: OK

## Response fields
- `resourceId` (integer)

## Example request (cURL)
```bash
curl -X POST \
  'http://localhost:8080/lokta-lms/api/v2/loanproducts' \
  -u '{username}:{password}' \
  -H 'Tenant-Identifier: default' \
  -H 'Content-Type: application/json' \
  -d '{
  "loanScheduleType": "CUMULATIVE",
  "accountMovesOutOfNPAOnlyOnArrearsCompletion": false,
  "accountingRule": 3,
  "allowApprovedDisbursedAmountsOverApplied": true,
  "allowAttributeOverrides": {
    "amortizationType": true,
    "graceOnArrearsAgeing": true,
    "graceOnPrincipalAndInterestPayment": true,
    "inArrearsTolerance": true
  },
  "allowCompoundingOnEod": false,
  "allowFullTermForTranche": false,
  "allowPartialPeriodInterestCalculation": true,
  "allowVariableInstallments": false,
  "amortizationType": 1
}'
```

## Example response
```json
{}
```

Interactive: https://developer.lokta.ai/reference.html#operation/createLoanProductV2
