npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hayeeder/fineract-api-client

v0.1.13

Published

TypeScript client for Fineract APIs

Readme

Fineract API Client

TypeScript client for interacting with specific Fineract client-related APIs. This library provides type-safe methods for creating and retrieving client information from a Fineract instance.

Installation

npm install @hayeeder/fineract-api-client
# or
yarn add @hayeeder/fineract-api-client

Usage

First, configure and create an instance of the FineractSDK:

import {
  createFineractSDK,
  FineractSDKConfig,
} from "@hayeeder/fineract-api-client";

const sdkConfig: FineractSDKConfig = {
  baseURL: "https://your-fineract-host.com", // Replace with your Fineract base URL
  tenantId: "default", // Your Fineract tenant ID
  username: "mifos", // Your Fineract username
  password: "password", // Your Fineract password
  timeout: 30000, // Optional: request timeout
};

const fineractSDK = createFineractSDK(sdkConfig);

Example 1: Create a New Client

import { CreateClientRequest } from "@hayeeder/fineract-api-client";

async function createNewClient() {
  const clientData: CreateClientRequest = {
    officeId: 1,
    fullname: "Jane Doe API",
    active: true,
    activationDate: "2024-07-28", // Use yyyy-MM-dd format
    dateFormat: "yyyy-MM-dd",
    locale: "en",
    legalFormId: 1, // Ensure this ID is valid in your Fineract setup
  };

  try {
    const response = await fineractSDK.client.createClient(clientData);
    console.log("Client Created:", response);
    // The actual ID of the created client is usually in response.resourceId or response.clientId
    const clientId = response.resourceId;
    if (clientId) {
      console.log("New Client ID:", clientId);
    }
  } catch (error) {
    console.error("Failed to create client:", error);
  }
}

createNewClient();

Example 2: Get Client Details

async function getClient(clientId: number) {
  try {
    const clientDetails = await fineractSDK.client.getClientDetails(clientId);
    console.log("Client Details:", clientDetails);
  } catch (error) {
    console.error(`Failed to get client ${clientId}:`, error);
  }
}

// Assuming a client with ID 123 exists
// getClient(123);

Complete Client Creation Workflow

To successfully create a client and set up all required accounts and details, follow these steps using the relevant APIs from the SDK:

Step 1. Create the SME Client

  • API: fineractSDK.sme.create()

  • Description: Use the SME API to create a new client (SME) in Fineract.

  • Example:

    const smeResponse = await fineractSDK.sme.create({
      officeId: 1, // Hardcoded to 1 because currently we only have head office
      fullname: "Acme Corporation",
      active: true, // Hardcoded to always true
      activationDate: "2024-07-28", // Use yyyy-MM-dd format i.e. can be Today's date
      dateFormat: "yyyy-MM-dd",
      locale: "en",
      legalFormId: 2, // Hardcoded to 2 meaning client in fineract langauge
      externalId: "LOS-EXT-123456", // Replace with actual externalId i.e. masterCompanyId from LOS
    });
    const clientExternalId = smeResponse.resourceExternalId; // external unique identifier i.e. externalId from LOS

2. Add Virtual Account Number (Datatables API)

  • API: fineractSDK.datatable.addEntry(datatableName, entryData)
  • Description: Add virtual account number for the client using the Datatables API. The datatable name and required fields depend on your Fineract configuration.
  • Example:
    // adding virtual account number
    await fineractSDK.datatable.addEntry("dt_client_virtual_account_number", {
      client_id: clientId, // get the clientId with fineractSDK.client.getClientDetails(externalId) API
      virtualAccountNumber: 123456, // replace with actual virtual account number from LOS
    });

2.1. Add Remitter Details (Datatables API) IMP: This step is only required for SMEs with RBF product enabled

  • API: fineractSDK.datatable.addEntry(datatableName, entryData)
  • Description: Add remitter details for the client using the Datatables API. The datatable name and required fields depend on your Fineract configuration.
  • Example:
    // adding remitter name
    await fineractSDK.datatable.addEntry("dt_client_remitter_names", {
      client_id: clientId, // get the clientId with fineractSDK.client.getClientDetails(externalId) API
      remitterName: "Remitter Name", // replace with actual remitter name from LOS
    });

2.2. Update Datatable Entry (Datatables API)

  • API: fineractSDK.datatable.updateEntry(datatableName, entityId, entryData)
  • Description: Update an existing datatable entry for a specific entity using the Datatables API. The datatable name, entity ID, and fields depend on your Fineract configuration.
  • Example:
    // updating virtual account number for a client
    await fineractSDK.datatable.updateEntry(
      "dt_client_virtual_account_number", // datatable name
      1635, // entityId (e.g., client ID)
      {
        virtual_account_number: "123123123123",
        locale: "en",
      }
    );

2.3. Get All Datatables (Datatables API)

  • API: fineractSDK.datatable.getAll()
  • Description: Retrieve a list of all datatables configured in Fineract. This is useful for discovering available datatables and their metadata.
  • Example:
    // get all datatables
    const datatables = await fineractSDK.datatable.getAll();
    console.log(datatables);

2.2. The 3-Step Process for Creating Savings Accounts

Creating any type of savings account (including Fallback, Cash Margin, or Collection accounts) is a three-step process. The account must be created, then approved, and finally activated to become fully functional.

  1. Create: Call fineractSDK.savingsaccounts.create() to create the account record. This returns a savingsId.
  2. Approve: Call fineractSDK.savingsproducts.approve() using the savingsId. This moves the account to an "approved" state.
  3. Activate: Call fineractSDK.savingsproducts.activate() using the savingsId. This makes the account "active" and ready for transactions.
  • API: fineractSDK.savingsaccounts.create(), fineractSDK.savingsproducts.approve(), fineractSDK.savingsproducts.activate()

  • Description: A complete workflow for creating, approving, and activating a savings account.

  • Example:

    // The ID of the client for whom the account is being created
    const clientId = 123;
    // The ID of the savings product (e.g., for a Fallback Account)
    const productId = 456;
    
    // Step 1: Create the savings account
    const savingsAccount = await fineractSDK.savingsaccounts.create({
      clientId: clientId,
      productId: productId,
      submittedOnDate: "2024-07-28", // Use current date
      dateFormat: "yyyy-MM-dd",
      locale: "en",
    });
    const savingsId = savingsAccount.savingsId;
    
    // Step 2: Approve the savings account
    const approvalResponse = await fineractSDK.savingsproducts.approve(
      savingsId,
      {
        approvedOnDate: "2024-07-28", // Use current date
        dateFormat: "yyyy-MM-dd",
        locale: "en",
      }
    );
    
    // Step 3: Activate the savings account
    const activationResponse = await fineractSDK.savingsproducts.activate(
      savingsId,
      {
        activatedOnDate: "2024-07-28", // Use current date
        dateFormat: "yyyy-MM-dd",
        locale: "en",
      }
    );
    
    console.log("Savings account created, approved, and activated successfully!");

3. Create Fallback Account (Savings Account API)

  • API: fineractSDK.savingsaccounts.create()
  • Description: Every SME should have one fallback account at the time of creation. It is type of savings account which will be used to store funds which cannot be categorised.
  • Example:
    const fallbackAccount = await fineractSDK.savingsaccounts.create({
      clientId: clientId, // get the clientId with fineractSDK.client.getClientDetails(externalId) API
      productId: FALLBACK_PRODUCT_ID, // get all savings products with fineractSDK.savingsproducts.getAll() and filter by shortName=FBSA i.e. fallback savings account to get productId to be sent
      submittedOnDate: "2024-07-28", // This should be current date
      dateFormat: "yyyy-MM-dd",
      locale: "en",
    });
    const fallbackAccountId = fallbackAccount.resourceId;
    Make sure to approve and activate the account as described above

4. Create Cash Margin Account (Savings Account API)

  • API: fineractSDK.savingsaccounts.create()
  • Description: Every SME should have cash margin account at the time of creation. It is type of savings account which will be used to store security deposits of 5%, 10% etc while disbursing loan
  • Example:
    const cashMarginAccount = await fineractSDK.savingsaccounts.create({
      clientId: clientId,
      productId: CASH_MARGIN_PRODUCT_ID, // get all savings products with fineractSDK.savingsproducts.getAll() and filter by shortName=CMSA i.e. cash margin savings account
      dateFormat: "yyyy-MM-dd",
      locale: "en",
    });
    const cashMarginAccountId = cashMarginAccount.resourceId;
    Make sure to approve and activate the account as described above

Summary of Steps and APIs:

  1. Create SME Client: fineractSDK.sme.create()
  2. Add Virtual Account & Remitter Details: fineractSDK.datatable.addEntry()
  3. Create Fallback Savings Account: fineractSDK.savingsaccounts.create()
  4. Create Cash Margin Savings Account: fineractSDK.savingsaccounts.create()

Note: Replace datatable names, product IDs, and field names as per your Fineract instance configuration.

Important API Paths: The API paths used in this SDK (/fineract-provider/api/v1/clients and /fineract-provider/api/v1/clients/{clientId}) are based on common Fineract setups and the provided clients.ts example. You must verify these paths against your Fineract instance's API documentation or the underlying ClientApi implementation you were using. You may need to adjust these paths in src/api/fineract-client-api.ts if they differ.

How to get repayment schedule without creating loan

1. Calculate Loan Repayment Schedule (Loan Accounts API)

  • API: fineractSDK.loansaccounts.getRepaymentSchedule()

  • Description: Calculate and retrieve the repayment schedule for a loan before actually creating the loan account. This is useful for showing the user the expected repayment plan based on their input parameters.

  • Example:

    const repaymentSchedule =
      await fineractSDK.loansaccounts.getRepaymentSchedule({
        productId: 1, // get all loan products with fineractSDK.loanproducts.getAll() and filter by externalId=REVENUE_BASED_FINANCING
        loanOfficerId: "",
        loanPurposeId: "",
        fundId: "",
        submittedOnDate: "28 May 2025", // Date the loan application is submitted
        expectedDisbursementDate: "30 May 2025", // Expected date of disbursement
        externalId: "",
        linkAccountId: "", // Optional but mandatory when createStandingInstructionAtDisbursement = true
        createStandingInstructionAtDisbursement: false, // Optional
        loanTermFrequency: 3, // Number of periods (e.g., months)
        loanTermFrequencyType: 2, // Frequency type (0=Days, 1=Weeks, 2=Months, 3=Years)
        numberOfRepayments: 3, // Total Number of installments to repay: Used like every [repaymentEvery] [repaymentFrequencyType] for [numberOfRepayments]
        repaymentEvery: 1, // Repayment interval (e.g., every 1 month)
        repaymentFrequencyType: 2, // Repayment frequency type (0=Days, 1=Weeks, 2=Months, 3=Years)
        repaymentFrequencyNthDayType: "",
        repaymentFrequencyDayOfWeekType: "",
        repaymentsStartingFromDate: null,
        interestChargedFromDate: null
        interestType: 0, // Interest type (0 = declining balance, 1 = flat)
        isEqualAmortization: false // Optional
        amortizationType: 1, // Amortization type (0=Equal principle payments, 1=Equal installments) - Fetch this from loan product details in amortizationType.id
        interestCalculationPeriodType: 3, // Interest calculation period type - Fetch this from loan product details in interestCalculationPeriodType.id
        loanIdToClose: "",
        isTopup: "",
        interestRatePerPeriod: 10, // Interest rate per period means 10%
        transactionProcessingStrategyCode: "mifos-standard-strategy", // Hardcoded to mifos-standard-strategy
        interestRateFrequencyType: 3 // hardcoded to 3 for now
        charges:[],
        collateral: [],
        dateFormat: "yyyy-MM-dd", // Hardcoded to yyyy-MM-dd - Date format used in the request
        locale: "en", // Locale
        clientId: 1, // // get the clientId with fineractSDK.client.getClientDetails(externalId) API
        loanType: "individual", // Type of loan
        principal: 100000, // Principal amount - Input from LOS
        allowPartialPeriodInterestCalcualtion: false // Optional
      });
    console.log(
      "Repayment Schedule:",
      JSON.stringify(repaymentSchedule, null, 2)
    );
  • API Path Used: /fineract-provider/api/v1/loans?command=calculateLoanSchedule

  • Reference: See src/api/fineract-loansaccounts-api.ts and src/types/loansaccounts.ts for full request/response details.

Note: This API does not create a loan, it only returns the calculated schedule based on the input parameters. Use this to preview repayment plans before submitting a loan application.

How to create loan application for SME

1. Create RBF collection account (Savings Account API)

  • API: fineractSDK.savingsaccounts.create()
  • Description: Every SME should have collection account based on product at the time of creation. It is type of savings account which will be save funds collected from SME. For each Loan Account i.e Loan Application, we must have corresponding collection account
  • Example:
    const cashMarginAccount = await fineractSDK.savingsaccounts.create({
      clientId: clientId,
      productId: RBF_COLLECTION_ACCOUNT, // get all savings products with fineractSDK.savingsproducts.getAll() and filter by shortName=RBF i.e. Rbf collection savings account
      dateFormat: "yyyy-MM-dd",
      locale: "en",
    });
    const cashMarginAccountId = cashMarginAccount.resourceId;
    Make sure to approve and activate the account as described above

2. Create Loan Account (Loan Accounts API)

  • API: fineractSDK.loansaccounts.create()

  • Description: Create a new loan account for a client in Fineract. This will actually create the loan in the system and return the created loan's details.

  • Example:

    const newLoan = await fineractSDK.loansaccounts.create({
      productId: 1, // get all loan products with fineractSDK.loanproducts.getAll() and filter by filter by externalId=REVENUE_BASED_FINANCING
      linkAccountId: 123, // ID of the collection account you created above
      submittedOnDate: "28 May 2025", // Date the loan application is submitted
      expectedDisbursementDate: "30 May 2025", // Expected date of disbursement - can be anything in future
      loanTermFrequency: 3, // Number of periods (e.g., months)
      loanTermFrequencyType: 2, // Frequency type (0=Days, 1=Weeks, 2=Months, 3=Years)
      numberOfRepayments: 3, // Total Number of installments to repay: Used like every [repaymentEvery] [repaymentFrequencyType] for [numberOfRepayments]
      repaymentEvery: 1, // Repayment interval (e.g., every 1 month)
      repaymentFrequencyType: 2, // Repayment frequency type (0=Days, 1=Weeks, 2=Months, 3=Years)
      interestType: 0, // Interest type (0 = flat, 1 = declining balance)
      amortizationType: 1, // Amortization type (0=Equal principle payments, 1=Equal installments) - Fetch this from loan product details in amortizationType.id
      interestCalculationPeriodType: 1, // Interest calculation period type - Fetch this from loan product details in interestCalculationPeriodType.id
      transactionProcessingStrategyCode: "mifos-standard-strategy", // Hardcoded to mifos-standard-strategy
      interestRatePerPeriod: 1, // Interest rate per period -> 1 means 10%
      dateFormat: "yyyy-MM-dd", // Hardcoded to yyyy-MM-dd - Date format used in the request
      locale: "en", // Locale
      clientId: 1, // // get the clientId with fineractSDK.client.getClientDetails(externalId) API
      loanType: "individual", // Type of loan
      principal: 100000, // Principal amount
    });
    console.log("Loan Created:", JSON.stringify(newLoan, null, 2));
  • API Path Used: /fineract-provider/api/v1/loans

  • Reference: See src/api/fineract-loansaccounts-api.ts and src/types/loansaccounts.ts for full request/response details.

Note: This API will create a loan account in Fineract. Make sure all required fields are provided and valid as per your Fineract instance configuration.

3. Approve Loan Account (Loan Accounts API)

  • API: fineractSDK.loansaccounts.approve()

  • Description: Approve a loan account in Fineract. This moves the loan from "Submitted and pending approval" status to "Approved" status, making it ready for disbursement.

  • Example:

    const approvalResponse = await fineractSDK.loansaccounts.approve(loanId, {
      approvedOnDate: "2024-07-30", // Date when the loan is approved
      expectedDisbursementDate: "2024-08-01", // Expected date for disbursement
      approvedLoanAmount: 100000, // Approved loan amount (can be same or different from requested)
      note: "Loan approved after credit assessment", // Optional approval note
      dateFormat: "yyyy-MM-dd", // Hardcoded to yyyy-MM-dd
      locale: "en", // Locale
    });
    console.log("Loan Approved:", JSON.stringify(approvalResponse, null, 2));
  • API Path Used: /fineract-provider/api/v1/loans/{loanId}?command=approve

  • Reference: See src/api/fineract-loansaccounts-api.ts and src/types/loansaccounts.ts for full request/response details.

Note: The loan must be in "Submitted and pending approval" status before it can be approved. After approval, the loan will be ready for disbursement.

How to get loan application data from Fineract to show on SME dashboard

  • API: fineractSDK.loanaccounts.getSummaryById()

  • Description: Get loan summary information from Fineract

  • Example:

    const loan = await fineractSDK.loanaccounts.getSummaryById(123);
    console.log("Loan:", JSON.stringify(loan, null, 2));
    
    const totalDisbursedAmount = loan.netDisbursalAmount;
    const totalOutstandingAmount = loan.summary.totalOutstanding;
    const interestRate = loan.annualInterestRate;
    const duration = loan.termFrequency;
    const durationUnit = loan.termPeriodFrequencyType.value;
    // To get next due payment
    const [year, month, day] = loan.delinquent.nextPaymentDueDate;

    This will return you the information of the loan summary you need to show on SME dashboard for a given loan application.

    How to get loan application schedule from Fineract to show on SME dashboard per loan application

  • API: fineractSDK.loanaccounts.getRepaymentScheduleById()

  • Description: Get loan repayment schedule information from Fineract

  • Example:

    const loan = await fineractSDK.loanaccounts.getRepaymentScheduleById(123);
    console.log("Loan:", JSON.stringify(loan, null, 2));
    
    const loanAmount = loan.approvedPrincipal;
    const totalOutstandingAmount = loan.summary.totalOutstanding;
    const interestRate = loan.annualInterestRate;
    const duration = loan.termFrequency;
    const durationUnit = loan.termPeriodFrequencyType.value;
    
    // To get Issued on date
    const [year, month, day] = loan.timeline.actualDisbursementDate;
    
    // Repayment periods, first item in the array is always the disbursement information which we ignore
    const schedule = loan.repaymentSchedule.periods;
    
    // Follow this for all items, except for the index 0
    const amount = schedule[1].totalInstallmentAmountForPeriod;
    const [dueYear, dueMonth, dueDay] = schedule[1].dueDate;
    const remaining = schedule[1].totalOutstandingForPeriod;
    const status = schedule[1].status;

    This will return you the information of the loan summary along with repayment schedule

How to create a deposit transaction for a savings account

  • API: fineractSDK.savingsaccounts.deposit()

  • Description: Create a deposit transaction for a specific savings account in Fineract. This allows you to add funds to an active savings account.

  • Example:

    const depositResponse = await fineractSDK.savingsaccounts.deposit(3873, {
      transactionDate: "11 August 2025",
      transactionAmount: 1000,
      paymentTypeId: 1,
      note: "this is 1000 deposit",
      dateFormat: "dd MMMM yyyy",
      locale: "en",
    });
    
    console.log("Deposit Response:", JSON.stringify(depositResponse, null, 2));
    
    // Access response data
    const officeId = depositResponse.officeId;
    const clientId = depositResponse.clientId;
    const savingsId = depositResponse.savingsId;
    const transactionId = depositResponse.resourceId;
    const paymentTypeId = depositResponse.changes.paymentTypeId;
  • API Path Used: /fineract-provider/api/v1/savingsaccounts/{accountId}/transactions?command=deposit

  • Required Fields:

    • transactionDate: Date of the transaction (string)
    • transactionAmount: Amount to deposit (number)
    • paymentTypeId: Payment type identifier (number)
    • dateFormat: Date format used (string)
    • locale: Locale for formatting (string)
  • Optional Fields:

    • note: Description or note for the transaction (string)

Note: The savings account must be in "Active" status before deposits can be made. Use the payment type IDs available in your Fineract instance.

How to get transaction template for a savings account

  • API: fineractSDK.savingsaccounts.getTransactionTemplate()

  • Description: Retrieve the transaction template for a savings account, which provides information about available payment types, currency details, and account information needed to create transactions.

  • Example:

    const template = await fineractSDK.savingsaccounts.getTransactionTemplate(3);
    
    console.log("Transaction Template:", JSON.stringify(template, null, 2));
    
    // Access template data
    const accountId = template.accountId;
    const accountNo = template.accountNo;
    const currency = template.currency;
    const paymentTypes = template.paymentTypeOptions;
    
    // Display currency information
    console.log(`Currency: ${currency.displayLabel}`); // e.g., "US Dollar ($)"
    console.log(`Currency Code: ${currency.code}`); // e.g., "USD"
    console.log(`Decimal Places: ${currency.decimalPlaces}`); // e.g., 2
    
    // List available payment types
    console.log("Available Payment Types:");
    paymentTypes.forEach((paymentType) => {
      console.log(`- ID: ${paymentType.id}, Name: ${paymentType.name}`);
      console.log(`  Description: ${paymentType.description}`);
      console.log(`  Cash Payment: ${paymentType.isCashPayment}`);
      console.log(`  System Defined: ${paymentType.isSystemDefined}`);
    });
    
    // Get current date information (useful for transaction dates)
    const [year, month, day] = template.date;
    const currentDate = new Date(year, month - 1, day); // Note: month is 0-indexed in JavaScript Date
  • API Path Used: /fineract-provider/api/v1/savingsaccounts/{accountId}/transactions/template

  • Returns:

    • Account information (ID, account number)
    • Currency details (code, name, symbol, decimal places)
    • Available payment type options
    • Current date and submission date information
    • Transaction flags (reversed, manual transaction, etc.)

Note: Use this endpoint to get the required information before creating deposit or withdrawal transactions. The payment type IDs returned here should be used in transaction requests.

How to get holidays information

  • API: fineractSDK.holidays.getHolidays() / fineractSDK.holidays.getHolidaysByOffice()

  • Description: Retrieve holidays from Fineract. This can be used to check for holidays when calculating loan repayment schedules or transaction dates. Only returns holidays with active status.

  • Example:

    // Get all active holidays
    const allHolidays = await fineractSDK.holidays.getHolidays();
    console.log("All Holidays:", JSON.stringify(allHolidays, null, 2));
    
    // Get active holidays for a specific office
    const officeHolidays = await fineractSDK.holidays.getHolidaysByOffice(1);
    console.log("Office Holidays:", JSON.stringify(officeHolidays, null, 2));
    
    // Process holiday information
    officeHolidays.forEach((holiday) => {
      const fromDate = `${holiday.fromDate[0]}-${holiday.fromDate[1]
        .toString()
        .padStart(2, "0")}-${holiday.fromDate[2].toString().padStart(2, "0")}`;
      const toDate = `${holiday.toDate[0]}-${holiday.toDate[1]
        .toString()
        .padStart(2, "0")}-${holiday.toDate[2].toString().padStart(2, "0")}`;
      const rescheduleTo = `${
        holiday.repaymentsRescheduledTo[0]
      }-${holiday.repaymentsRescheduledTo[1]
        .toString()
        .padStart(2, "0")}-${holiday.repaymentsRescheduledTo[2]
        .toString()
        .padStart(2, "0")}`;
    
      console.log(`Holiday: ${holiday.name}`);
      console.log(`  From: ${fromDate} to ${toDate}`);
      console.log(`  Status: ${holiday.status.value}`);
      console.log(`  Repayments rescheduled to: ${rescheduleTo}`);
      console.log(`  Rescheduling Type: ${holiday.reschedulingType}`);
    });
  • API Path Used: /fineract-provider/api/v1/holidays (with optional ?officeId= parameter)

  • Returns:

    • Array of holiday objects with the following properties:
      • id: Holiday ID
      • name: Holiday name
      • description: Holiday description
      • fromDate: Start date as [year, month, day] array
      • toDate: End date as [year, month, day] array
      • repaymentsRescheduledTo: Rescheduled date as [year, month, day] array
      • status: Status object with id, code, and value
      • reschedulingType: Type of rescheduling (numeric)

Note: This API automatically filters to only return holidays with status.code === "holidayStatusType.active". Use this information to determine if a date falls on a holiday and when repayments should be rescheduled to.

How to get working days information

  • API: fineractSDK.workingdays.getWorkingDays()

  • Description: Retrieve the working days configuration from Fineract. This returns a formatted object indicating which days of the week are considered working days. This information is useful for calculating business days, scheduling loan repayments, and determining when transactions can be processed.

  • Example:

    // Get working days configuration
    const workingDays = await fineractSDK.workingdays.getWorkingDays();
    console.log("Working Days:", JSON.stringify(workingDays, null, 2));
    
    // Example output:
    // {
    //   "Monday": true,
    //   "Tuesday": true,
    //   "Wednesday": true,
    //   "Thursday": true,
    //   "Friday": true,
    //   "Saturday": false,
    //   "Sunday": false
    // }
    
    // Check if a specific day is a working day
    if (workingDays.Monday) {
      console.log("Monday is a working day");
    }
    
    // Get list of working days
    const workingDayNames = Object.entries(workingDays)
      .filter(([day, isWorking]) => isWorking)
      .map(([day]) => day);
    console.log("Working days:", workingDayNames.join(", "));
    
    // Get list of non-working days (weekends)
    const nonWorkingDays = Object.entries(workingDays)
      .filter(([day, isWorking]) => !isWorking)
      .map(([day]) => day);
    console.log("Non-working days:", nonWorkingDays.join(", "));
  • API Path Used: /fineract-provider/api/v1/workingdays

  • Returns:

    • Object with day names as keys and boolean values indicating if it's a working day:
      • Monday: boolean
      • Tuesday: boolean
      • Wednesday: boolean
      • Thursday: boolean
      • Friday: boolean
      • Saturday: boolean
      • Sunday: boolean

Note: This API automatically parses the RRULE recurrence string from Fineract and converts it to a user-friendly format. The original response contains technical details like repayment rescheduling options, but this API focuses on the essential working days information.

How to get available charges

  • API: fineractSDK.creditlines.getCharges() / fineractSDK.creditlines.getCreditLineCharges()

  • Description: Retrieve available charges from Fineract that can be applied to credit lines. This helps users discover which charges are available before creating a credit line.

  • Example:

    // Get all available charges
    const allCharges = await fineractSDK.creditlines.getCharges();
    console.log("All Charges:", JSON.stringify(allCharges, null, 2));
    
    // Get only charges applicable to Line of Credit and Loans
    const creditLineCharges =
      await fineractSDK.creditlines.getCreditLineCharges();
    console.log(
      "Credit Line Charges:",
      JSON.stringify(creditLineCharges, null, 2)
    );
    
    // Filter charges with parameters
    const activeCharges = await fineractSDK.creditlines.getCharges({
      active: true,
      chargeAppliesTo: "lineOfCredit",
    });
    
    // Process charges to show available options
    creditLineCharges.forEach((charge) => {
      console.log(`Charge: ${charge.name}`);
      console.log(`  ID: ${charge.id}`);
      console.log(`  Amount: ${charge.amount} ${charge.currency.code}`);
      console.log(`  Type: ${charge.chargeCalculationType.value}`);
      console.log(`  Applies to: ${charge.chargeAppliesTo.value}`);
      console.log(`  Time: ${charge.chargeTimeType.value}`);
      console.log(`  Active: ${charge.active}`);
      console.log(`  Penalty: ${charge.penalty}`);
      if (charge.taxGroup) {
        console.log(`  Tax Group: ${charge.taxGroup.name}`);
      }
      console.log("---");
    });
    
    // Select charges for credit line creation
    const selectedCharges = creditLineCharges
      .filter(
        (charge) => charge.chargeAppliesTo.code === "chargeAppliesTo.lineOfCredit"
      )
      .map((charge) => ({
        ...charge,
        editableAmount: charge.amount, // Set editable amount
      }));
  • API Path Used: /fineract-provider/api/v1/charges

  • Available Methods:

    • getCharges(params?): Get all charges with optional filtering
    • getCreditLineCharges(): Get charges applicable to Line of Credit and Loans only
  • Optional Parameters:

    • chargeAppliesTo: Filter by what the charge applies to ('loan', 'savings', 'lineOfCredit')
    • active: Filter by active status (boolean)
  • Returns:

    • Array of charge objects with detailed information including:
      • Basic info: id, name, active, penalty
      • Financial: amount, currency, chargeCalculationType
      • Application: chargeAppliesTo, chargeTimeType, chargePaymentMode
      • Tax: taxGroup (optional)
      • Fees: feeInterval, feeFrequency (for recurring charges)

Note: Use getCreditLineCharges() to get pre-filtered charges suitable for credit lines. The charges returned can be directly used in the CreateCreditLineRequest.charges array after adding the editableAmount property.

How to get credit line template options

  • API: fineractSDK.creditlines.getCreditLineTemplate() and individual option getters

  • Description: Retrieve pre-defined template options for credit line creation. This provides deterministic values for fields like cash margin type, product type, review periods, etc., ensuring you use valid values that exist in the system.

  • Example:

    const clientId = 6029; // Replace with actual client ID
    
    // Get the complete template with all options
    const template = await fineractSDK.creditlines.getCreditLineTemplate(
      clientId
    );
    console.log("Full template:", JSON.stringify(template, null, 2));
    
    // Or get specific options individually
    const cashMarginTypeOptions =
      await fineractSDK.creditlines.getCreditLineCashMarginTypeOptions(clientId);
    const productTypeOptions =
      await fineractSDK.creditlines.getCreditLineProductTypeOptions(clientId);
    const reviewPeriodOptions =
      await fineractSDK.creditlines.getCreditLineReviewPeriodOptions(clientId);
    const interestChargeTimeOptions =
      await fineractSDK.creditlines.getCreditLineInterestChargeTimeOptions(
        clientId
      );
    const loanOfficers = await fineractSDK.creditlines.getCreditLineLoanOfficers(
      clientId
    );
    
    // Display available options
    console.log("Cash Margin Type Options:");
    cashMarginTypeOptions.forEach((option) => {
      console.log(
        `- ID: ${option.id}, Code: ${option.code}, Value: ${option.value}`
      );
    });
    // Example output:
    // - ID: 1, Code: PERCENTAGE, Value: Percentage
    // - ID: 2, Code: FLAT, Value: Flat
    
    // Select options deterministically for credit line creation
    const selectedCashMarginType = cashMarginTypeOptions.find(
      (option) => option.code === "PERCENTAGE"
    );
    const selectedProductType = productTypeOptions.find(
      (option) => option.code === "RECEIVABLE"
    );
    const selectedReviewPeriod = reviewPeriodOptions.find(
      (option) => option.code === "locreviewperiod.6months"
    );
    const selectedLoanOfficer = loanOfficers.find(
      (officer) => officer.isActive && officer.isLoanOfficer
    );
    
    // Use the selected IDs in your credit line creation request
    const creditLineData = {
      productType: selectedProductType?.id || 1,
      cashMarginType: selectedCashMarginType?.id || 1,
      reviewPeriod: selectedReviewPeriod?.id || 6,
      loanOfficerId: selectedLoanOfficer?.id || 1,
      // ... other fields
    };
  • API Path Used: /fineract-provider/api/v1/clients/{clientId}/creditlines/template

  • Available Methods:

    • getCreditLineTemplate(clientId): Get complete template with all options
    • getCreditLineStatusOptions(clientId): Get status options only
    • getCreditLineProductTypeOptions(clientId): Get product type options only
    • getCreditLineReviewPeriodOptions(clientId): Get review period options only
    • getCreditLineCashMarginTypeOptions(clientId): Get cash margin type options only
    • getCreditLineInterestChargeTimeOptions(clientId): Get interest charge time options only
    • getCreditLineLoanOfficers(clientId): Get available loan officers only
  • Returns:

    • Complete template object with all available options
    • Individual methods return specific option arrays
    • Each option contains: id, code, value
    • Loan officers include additional details: displayName, officeId, officeName, etc.

Best Practice: Always use the template endpoints to get valid option values before creating credit lines. This ensures you're using IDs that exist in the system and prevents validation errors. The template approach makes credit line creation deterministic and reduces API failures.

How to create a credit line

  • API: fineractSDK.creditlines.createCreditLine()

  • Description: Create a new credit line for a client in Fineract. This API handles the creation of Line of Credit (LOC) facilities with comprehensive configuration including charges, approved buyers, and settlement accounts. The recommended approach is to use template APIs to get deterministic values.

  • Example:

    import { CreateCreditLineRequest } from "@hayeeder/fineract-api-client";
    
    const clientId = 6029; // Replace with actual client ID
    
    // Step 1: Get template options for deterministic value selection
    const cashMarginTypeOptions =
      await fineractSDK.creditlines.getCreditLineCashMarginTypeOptions(clientId);
    const productTypeOptions =
      await fineractSDK.creditlines.getCreditLineProductTypeOptions(clientId);
    const reviewPeriodOptions =
      await fineractSDK.creditlines.getCreditLineReviewPeriodOptions(clientId);
    const interestChargeTimeOptions =
      await fineractSDK.creditlines.getCreditLineInterestChargeTimeOptions(
        clientId
      );
    const loanOfficers = await fineractSDK.creditlines.getCreditLineLoanOfficers(
      clientId
    );
    
    // Step 2: Get available charges
    const availableCharges = await fineractSDK.creditlines.getCreditLineCharges();
    
    // Step 3: Select values based on your business requirements
    const selectedProductType =
      productTypeOptions.find(
        (option) => option.code === "RECEIVABLE" || option.id === 1
      ) || productTypeOptions[0];
    
    const selectedCashMarginType =
      cashMarginTypeOptions.find(
        (option) => option.code === "PERCENTAGE" || option.id === 1
      ) || cashMarginTypeOptions[0];
    
    const selectedReviewPeriod =
      reviewPeriodOptions.find(
        (option) => option.code === "locreviewperiod.6months" || option.id === 6
      ) || reviewPeriodOptions[0];
    
    const selectedInterestChargeTime =
      interestChargeTimeOptions.find(
        (option) => option.code === "UPFRONT" || option.id === 1
      ) || interestChargeTimeOptions[0];
    
    const selectedLoanOfficer =
      loanOfficers.find(
        (officer) => officer.isActive && officer.isLoanOfficer && officer.id === 1
      ) ||
      loanOfficers.find((officer) => officer.isActive && officer.isLoanOfficer);
    
    const selectedCharges = availableCharges
      .filter(
        (charge) =>
          charge.active &&
          charge.chargeTimeType.code === "chargeTimeType.lineOfCreditActivation"
      )
      .slice(0, 2)
      .map((charge) => ({
        ...charge,
        editableAmount: charge.amount,
      }));
    
    // Step 4: Create credit line with template-derived values
    const creditLineData: CreateCreditLineRequest = {
      productType: selectedProductType?.id || 1,
      currencyCode: "AED",
      clientCompanyName: "Test Company Ltd",
      clientContactPersonName: "John Doe",
      clientContactPersonPhone: "0501234567",
      clientContactPersonEmail: "[email protected]",
      authorizedSignatoryName: "Jane Smith",
      authorizedSignatoryPhone: "0507654321",
      authorizedSignatoryEmail: "[email protected]",
      virtualAccount: "VA2000",
      externalId: "CL-TEST-001",
      specialConditions: "Test credit line with standard terms",
      maxCreditLimit: "1000000",
      reviewPeriod: selectedReviewPeriod?.id || 6,
      interimReviewDate: "15 June 2026",
      annualInterestRate: 1200, // 12% in basis points
      tenorDays: 365,
      advancePercentage: "85",
      cashMarginType: selectedCashMarginType?.id || 1,
      cashMarginValue: 15,
      interestChargeTime: selectedInterestChargeTime?.id || 1,
      loanOfficerId: selectedLoanOfficer?.id || 1,
      distributionPartner: "Test Distribution Partner",
      approvedBuyers: [
        { name: "Approved Buyer One" },
        { name: "Approved Buyer Two" },
      ],
      settlementSavingsAccountId: 7135,
      charges: selectedCharges, // Use template-derived charges
      dateFormat: "dd MMMM yyyy",
      locale: "en",
    };
    
    // Step 5: Create the credit line
    const response = await fineractSDK.creditlines.createCreditLine(
      clientId,
      creditLineData
    );
    console.log("Credit Line Created:", JSON.stringify(response, null, 2));
    console.log("Credit Line Resource ID:", response.resourceId);
  • API Path Used: /fineract-provider/api/v1/clients/{clientId}/creditlines

  • Parameters:

    • clientId: The ID of the client to create the credit line for
    • creditLineData: Comprehensive credit line configuration object
  • Required Fields:

    • Basic Info: productType, currencyCode, clientCompanyName
    • Contact Details: clientContactPersonName, clientContactPersonPhone, clientContactPersonEmail
    • Signatory: authorizedSignatoryName, authorizedSignatoryPhone, authorizedSignatoryEmail
    • Financial: maxCreditLimit, annualInterestRate, settlementSavingsAccountId
    • System: externalId, virtualAccount, dateFormat, locale

Best Practice Workflow:

  1. Get Template Options: Use template APIs to retrieve valid option values
  2. Get Available Charges: Use charges API to get applicable charges
  3. Filter and Select: Choose values based on your business logic with fallbacks
  4. Create with Confidence: Use the selected IDs in your credit line creation request

Why This Approach: This method ensures you're using valid, current system values, prevents validation errors, and makes your credit line creation deterministic and reliable.

Template-Based Value Selection Benefits:

  • Deterministic: No guessing of field values or IDs
  • Validation-Safe: Uses only valid system values
  • Future-Proof: Adapts to changes in your Fineract configuration
  • Error-Reduced: Eliminates common field validation failures
  • Business-Aligned: Select values based on your business logic

Note: This API creates a comprehensive Line of Credit facility with all associated charges and configurations. The template-based approach ensures all field values are valid for your Fineract instance.

Retrieving Credit Lines

Get all credit lines for a client, including associated loan details:

  • API: fineractSDK.creditlines.getCreditLines(clientId)

    // Get all credit lines for a client
    const clientId = 123;
    const creditLines = await fineractSDK.creditlines.getCreditLines(clientId);
    
    console.log(`Found ${creditLines.length} credit line(s)`);
    
    // Process each credit line
    for (const creditLineWithLoans of creditLines) {
      const creditLine = creditLineWithLoans.lineOfCredit;
    
      console.log(`Credit Line ID: ${creditLine.id}`);
      console.log(`Account Number: ${creditLine.accountNumber}`);
      console.log(`Maximum Amount: ${creditLine.maximumAmount}`);
      console.log(`Available Balance: ${creditLine.availableBalance}`);
      console.log(`Consumed Amount: ${creditLine.consumedAmount}`);
      console.log(`Status: ${creditLine.status.value}`);
      console.log(`Product Type: ${creditLine.productType}`);
    
      // Process associated loans
      if (creditLineWithLoans.loans && creditLineWithLoans.loans.length > 0) {
        console.log(`Associated Loans (${creditLineWithLoans.loans.length}):`);
    
        for (const loan of creditLineWithLoans.loans) {
          console.log(`  Loan #${loan.accountNo} - ${loan.productName}`);
          console.log(`  Status: ${loan.status.value}`);
          console.log(`  Loan Balance: ${loan.loanBalance || 0}`);
          console.log(`  Amount Paid: ${loan.amountPaid || 0}`);
          console.log(`  In Arrears: ${loan.inArrears ? "Yes" : "No"}`);
    
          // Timeline information
          if (loan.timeline) {
            if (loan.timeline.submittedOnDate) {
              console.log(
                `  Submitted: ${loan.timeline.submittedOnDate.join("-")}`
              );
            }
            if (loan.timeline.approvedOnDate) {
              console.log(
                `  Approved: ${loan.timeline.approvedOnDate.join("-")}`
              );
            }
            if (loan.timeline.disbursementDate) {
              console.log(
                `  Disbursed: ${loan.timeline.disbursementDate.join("-")}`
              );
            }
          }
        }
      } else {
        console.log("No associated loans.");
      }
    }
  • API Path Used: /fineract-provider/api/v1/clients/{clientId}/creditlines

  • Parameters:

    • clientId: The ID of the client to retrieve credit lines for
  • Returns: Array of CreditLineWithLoans objects containing:

    • Credit Line Info: id, accountNumber, productType, maximumAmount, availableBalance, consumedAmount, status, externalId
    • Associated Loans: Array of loan objects with account details, balances, status, and timeline information
  • Response Structure:

    interface CreditLineWithLoans {
      lineOfCredit: LineOfCredit;
      loans: CreditLineLoan[];
    }

Note: This endpoint provides a comprehensive view of all credit lines and their associated loans, making it ideal for portfolio management and client relationship oversight.

Development

Run Reports Overview

The Fineract "run reports" API returns tabular datasets (headers + positional rows). This SDK exposes three curated report wrappers and returns a consumer-friendly mapped structure:

  1. getLoanPaymentsDue
  2. getLoanPaymentsDueOverdue
  3. getExpectedPaymentsByDateBasic

Mapped response shape:

interface MappedReportResponse {
  data: Array<Record<string, any>>; // each row keyed by columnName
}

Key Points:

  • Report names are matched exactly (case and spaces) and URL-encoded automatically.
  • Rows are already mapped to objects; you can iterate report.data directly.
  • Any date arrays like [YYYY, M, D] are still raw; convert if needed (kept unmodified for neutrality).
  • Office and loan officer filters are fixed (see rationale below).
  • Installment, overdue, and date range parameters let you tailor dataset size and analytical focus.

Parameter Rationale & Fixed Filters

The office filter is currently fixed to head office (id = 1) and the loan officer filter to all officers (value -1) because present usage is single-office and portfolio-wide. This keeps the surface small and avoids accidental under-filtering. If multi-office support becomes necessary later, these can be introduced as explicit method parameters in a versioned update.

Installment & Overdue Window Parameters

Some report parameters control which installments (scheduled repayments) or overdue windows are included:

  • fromInstallment / toInstallment: Inclusive installment sequence bounds. Each repayment period is numbered starting at 1. Setting both to the same value isolates a single installment; widening the range lets you retrieve multiple sequential installments (e.g. 1–3 for the first three periods). Use these to:
    • Reduce payload size for dashboard widgets (single next due installment).
    • Segment near-term vs. mid-term expected cash flows (e.g. 1–2 vs. 3–6).
    • Emulate paging by requesting adjacent windows (1–3, then 4–6).
    • Target analysis of a problematic period by narrowing to its sequence number.
  • overdueFromDays / overdueToDays: Inclusive day-overdue bounds (used in the overdue variant). They filter installments whose due date has passed by a number of days in the specified range (e.g. 1–30 days overdue). Use these to:
    • Focus collection efforts on a specific aging bucket (e.g. 1–7 early delinquency vs. 30–60 moderate delinquency).
    • Build aging tiers by running the report multiple times with different windows.
    • Avoid pulling very old irrecoverable items unless explicitly needed.

Tips:

  1. Start narrow (e.g. single installment or small overdue window) for fast UI loads; expand only when users request detail.
  2. Wide ranges (large installment span or 1–365 overdue) increase latency and payload size—consider batching.
  3. Combining installment and overdue ranges lets you zoom into a specific slice (e.g. installment 2 only, overdue 15–30 days).
  4. If fromInstallment > toInstallment or overdueFromDays > overdueToDays, the underlying report will typically return empty results (or error depending on configuration).

These parameters exist to let you shape result size and focus. Future filters (e.g., dynamic officer/office filtering) would work alongside them.

Date Range Parameters (Friendly Names)

  • startDate / endDate: Inclusive due-date window for expected payments. Must match dateFormat.
  • locale and dateFormat: these are required by Fineract to parse the dates.

Loan Payments Due

  • API: fineractSDK.reports.getLoanPaymentsDue()
  • Description: Retrieves loans with installments in the specified installment sequence window. Response is mapped (each row keyed by column name).
  • Example:
import { LoanPaymentsDueParams } from "@hayeeder/fineract-api-client";

const params: LoanPaymentsDueParams = {
  fromInstallment: 2, // Start from installment sequence 2 (inclusive)
  toInstallment: 2, // End at installment sequence 2 (single period)
}; // officeId=1 & loanOfficerId=-1 auto-injected

const report = await fineractSDK.reports.getLoanPaymentsDue(params);
console.log(report.data[0]); // { loanId: 123, dueAmount: 500, ... }

Notes:

  1. Use fromInstallment / toInstallment to define an inclusive installment sequence range; keeping them equal isolates a single installment.
  2. Response rows are already mapped to objects keyed by column name (no header parsing needed).
  3. Office and loan officer filters are auto-applied; you don't need to supply them.
  4. Larger installment ranges increase payload size and latency—prefer narrow windows for dashboards.
  5. Some dates may appear as [year, month, day] arrays.

Loan Payments Due (Overdue Loans)

  • API: fineractSDK.reports.getLoanPaymentsDueOverdue()
  • Description: Same as the base variant but constrained to a day-overdue window, returning only installments whose due date has passed within the specified overdue day range.
  • Example:
import { LoanPaymentsDueOverdueParams } from "@hayeeder/fineract-api-client";

const params: LoanPaymentsDueOverdueParams = {
  fromInstallment: 1, // Start from installment sequence 1 (first due installment)
  toInstallment: 1, // Single installment
  overdueFromDays: 1, // Minimum days overdue (>=1 day past due)
  overdueToDays: 30, // Maximum days overdue (<=30 days past due)
}; // officeId=1 & loanOfficerId=-1 auto-injected

const report = await fineractSDK.reports.getLoanPaymentsDueOverdue(params);
console.log(report.data[0]); // { loanId: 456, overdueDays: 7, ... }

Notes:

  1. Adds overdue day window via overdueFromDays / overdueToDays (inclusive). Setting both equal isolates a single aging bucket.
  2. You can still narrow installments with fromInstallment / toInstallment simultaneously (e.g. next installment only, overdue 1–7 days).
  3. Office and loan officer filters are auto-applied (single-office, portfolio-wide context).
  4. Mapping and date-array behavior match the base variant.

Expected Payments By Date - Basic

  • API: fineractSDK.reports.getExpectedPaymentsByDateBasic()
  • Description: Returns expected payments scheduled between a start and end date. Response rows are already keyed.
  • Example:
import { ExpectedPaymentsByDateBasicParams } from "@hayeeder/fineract-api-client";

const params: ExpectedPaymentsByDateBasicParams = {
  startDate: "2025-02-04",
  endDate: "2025-10-23",
  locale: "en",
  dateFormat: "yyyy-MM-dd",
}; // officeId=1 & loanOfficerId=-1 auto-injected

const report = await fineractSDK.reports.getExpectedPaymentsByDateBasic(params);
console.log(report.data[0]); // { loanId: 789, dueDate: "2025-02-04", ... }

Notes:

  1. Provide startDate / endDate in the supplied dateFormat (e.g. yyyy-MM-dd).
  2. locale and dateFormat are required so the backend can parse your dates.
  3. Narrow date windows (e.g. a week) for fast UI loads; expand only when exporting or drilling deeper.
  4. Office and loan officer filters are applied automatically.
  5. Any date arrays that appear in nested structures follow [year, month, day] ordering.
  • Build: npm run build
  • Test: npm run test
  • Lint: npm run lint
  • Run Examples: npm run dev (Ensure you update src/examples/run-examples.ts with your Fineract instance details)
  • Generate Docs: npm run build:docs (Output in docs/ folder)

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

Please make sure to update tests as appropriate.

License

MIT