application/json. Send every money and rate field as a decimal string, never as a JSON number.
Complete the Prerequisites first. Send the same bearer token on all six calls. Lender takes the officer from the token subject. Under the generic profile, only that officer can approve and disburse the application.
Use jurisdiction
XX for this walkthrough. XX is Lender’s generic profile, and it computes no withholdings — which keeps the amounts in step 6 simple. A Brazilian regulated loan carries CET disclosure and capitalization consent. Read the Brazil regulatory pack for that path. A payroll-deducted contract belongs to the consignado privado bounded context. Read Consignado privado for its vocabulary and its lifecycle topics.The six calls
1. Create the product
POST /api/v1/loan-products
loanType takes personal, commercial, or card. jurisdictionCode takes a code the registry carries: XX or BR. Any other code returns 422.
The response carries the new id. The product is in draft, which is all this walkthrough needs: an application binds to a version, so you do not activate the product to originate.
2. Create a version
POST /api/v1/loan-products/{productId}/versions
currencyhas no default. Supply a valid ISO-4217 code in uppercase. The version is the currency source of truth from here to the ledger.- A fixed version carries no floating linkage. With
rateMode: fixed, omitfloatingRateTableId,floatingSpreadBps, andrequiresFloatingRate. Lender rejects a fixed version that carries any of them. jurisdictionCodeis required, and it must match the product’s. A version cannot move a product to another jurisdiction.
versionId.
3. Bind an accounting profile
POST /api/v1/loan-products/{productId}/accounting-profiles
The profile maps each accounting event onto general-ledger accounts. Lender needs it at disbursement, so bind it now.
disbursement, repayment, prepayment, accrual, collection_unapplied, collection_reapply, and collection_refund. accrual_tax is the optional eighth. Fewer than seven rules is rejected before the handler runs.
Each leg declares exactly one of role or component, and no account appears on both sides of the same rule. Four events also carry a fixed leg shape:
collection_reapply takes unapplied_cash as its only debit. Each credit it carries must also appear as a credit on repayment or prepayment, with the same account and the same role. disbursement and repayment each need at least one debit and one credit.
Keep the disbursement rule at two legs, as above. Lender fills the cash leg with the net amount and every other structural leg with the gross amount. A component leg takes a computed withholding, and the generic profile computes none, so the two-leg rule is the one that balances under XX.
In multi-tenant mode the profile also needs midazOrganizationId and midazLedgerId. Read Define a loan product for the wider product surface.
4. Create the application
POST /api/v1/loan-applications
requestedInterestRateis the monthly rate as a decimal string at scale 8. It must be greater than0and no greater than1. Lender builds the schedule from this rate:"0.01500000"a month is1800bps a year.requestedInstallmentsis between 1 and 600.previewScheduleSnapshotIdis a UUID you generate to identify the quote you showed the borrower. Lender records it on the application. Compute the schedule you show withPOST /api/v1/loan-applications/preview-schedule. That call persists nothing and returns no identifier. Mint the identifier on your side and keep it with your own quote record.- There is no
assignedOfficerIdfield. Lender sets it from the token subject.
pending_approval, carrying the jurisdiction code and profile version Lender resolved from the product version.
5. Approve
POST /api/v1/loan-applications/{id}/approve
approvedAmount becomes the ceiling on everything you disburse. Under XX, only the officer who created the application may approve it. The application moves to approved and carries the decision record.
6. Disburse
POST /api/v1/loan-applications/{id}/disburse
Send the header X-Idempotency. Lender requires it. A retry with the same value replays the first response, and records no second disbursement.
loanAccountId is a UUID you supply — Lender does not mint it. It identifies the loan account this contract services under, and it is immutable across later tranches of the same application.
Lender also checks that:
netDeliveredAmountdoes not exceedgrossRequestedAmount.grossRequestedAmount, and the running total across tranches, does not exceedapprovedAmount.disbursedAtis not before the approval decision.- The jurisdiction and profile version still match the pair Lender resolved at step 4.
originationFeeAmount is optional. It carries cost metadata that accrual reads. Lender does not treat it as a withholding, so it does not change the net-to-gross relationship.
Confirm the loan exists
The disburse response carries the application in
disbursed with its disbursement event. Then read the loan account:
The response also carries
profileVersion — the jurisdiction profile version, not a product version. It carries no currency. The loan uses the currency you set on the loan product version in step 1. Keep that value with your own product record.
If you configured Midaz, the disbursement posting reaches the ledger once the outbox dispatcher relays the intent. That happens shortly after the call, not inside it.
Next steps
Service a loan
Record repayments, prepay, reschedule, and correct a live loan account.
How accrual works
Interest recognition runs on its own schedule. Start an accrual run, or enable the heartbeat.

