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 seven calls. Lender takes the officer from the token subject, and only that officer can approve and disburse the application.
This walkthrough books a Brazilian loan with tax regime
none, which computes no IOF and keeps the amounts in step 7 simple. Read the Brazil regulatory pack for a product that withholds IOF. A payroll-deducted contract belongs to the consignado privado bounded context. Read Consignado privado for its vocabulary and its lifecycle topics.The seven calls
1. Create the product
POST /api/v1/loan-products
loanType takes personal, commercial, or card. jurisdictionCode is BR. Any other code returns 422.
The response carries the new id. The product is in draft.
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.rateBandis required. It is the price band the version authorizes, in annual basis points. An application whose rate falls outside it is refused.admittedBorrowerKindsnames who the version lends to. An empty or omitted list admits no borrower.normPortfolioClassis the carteira. A Brazilian version without one cannot be activated in step 4.- 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. Activation refuses a product without one, so bind it now.
disbursement, repayment, prepayment, accrual, collection_unapplied, collection_reapply, and collection_refund. The other event types are optional. Fewer than seven rules is rejected before the handler runs. A product under Brazilian credit-loss rules also needs provision_constitution and provision_reversal, or its measured provision does not post to the ledger. Activation does not check them.
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 tax regime none computes none, so the two-leg rule is the one that balances in this walkthrough.
In multi-tenant mode the profile also needs midazOrganizationId and midazLedgerId. Read Define a loan product for the wider product surface.
4. Activate the product
POST /api/v1/loan-products/{productId}/activate
5. Create the application
POST /api/v1/loan-applications
Send the header X-Idempotency. Lender requires it on this call and on steps 6 and 7. A retry with the same value replays the first response and writes nothing new.
borrowerIdis a UUID.borrowerKindtakespessoaFisicaorpessoaJuridica, and the version must admit it.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 and compares it with the version’s rate band in annual basis points:"0.01500000"a month is1800bps a year, inside the band from step 2.requestedInstallmentsis between 1 and 600.previewScheduleSnapshotIdis optional. To cite the quote you showed the borrower, send thescheduleOfferSnapshotIdthatPOST /api/v1/loan-applications/preview-schedulereturned.- 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.
6. Approve
POST /api/v1/loan-applications/{id}/approve
approvedAmount becomes the ceiling on everything you disburse. The application moves to approved and carries the decision record.
7. Disburse
POST /api/v1/loan-applications/{id}/disburse
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. jurisdictionExtensions carries what the Brazilian pipeline reads: the disbursement instant, the version from step 2, and this call’s X-Idempotency value as requestId. The disbursement fails when one is absent.
Lender also checks that:
netDeliveredAmountdoes not exceedgrossRequestedAmount.grossRequestedAmountequalsapprovedAmount, because this is the application’s only disbursement. A release in tranches declarestrancheNumber, and its running total does not exceedapprovedAmount.decisionAtanddisbursedAtare not before the application’s creation, anddisbursedAtis not before the approval decision.- The jurisdiction and profile version still match the pair Lender resolved at step 5.
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 2.
If you configured the Midaz ledger relay, the disbursement posting reaches the ledger when the outbox dispatcher relays the intent. That happens shortly after the call, not inside it. Otherwise the intent remains in the outbox.
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.

