Skip to content

Apache Fineract Datatables Extensibility Guide

Datatables are Apache Fineract's built-in mechanism for attaching custom data to core entities. You create a new table, register it against a core application table (for example m_client or m_loan), and Fineract exposes a full CRUD API for that table automatically - including permission checks and data scoping within the user's office hierarchy.

No schema migrations, no code changes, and no application restarts are required. Datatables are created and managed entirely through the REST API.

What Datatables Are

A datatable is a MySQL/MariaDB or PostgreSQL table that has a foreign key relationship to one of the Fineract core entity tables. When you register a datatable against m_client, for example, every row in your table is linked to a specific client record by its client_id.

The Fineract API then provides read, create, update, and delete endpoints for entries in that table, scoped to the linked entity. Permission checks follow the same role-based access control used by the rest of the platform.

The Fineract Reference Application's stretchy datatables UI is built on top of this same API.

Supported Application Tables

A datatable must be linked to one of the following core Fineract tables:

apptableNameEntity
m_clientClients
m_groupGroups
m_loanLoans
m_officeOffices
m_saving_accountSavings accounts
m_product_loanLoan products
m_savings_productSavings products

Column Types

Each column in a datatable must have one of the following types:

TypeNotes
BooleanTrue/false value
DateDate only, no time component
DateTimeDate and time
DecimalFloating-point numeric
DropdownForeign key into a Fineract code value list. Requires code field (mandatory). Column name becomes code_cd_<name>
NumberInteger numeric
StringVariable-length text. Requires length field (mandatory)
TextUnlimited text (LONGTEXT)

Column names may contain alphanumeric characters, underscores, and spaces. They cannot start with a number, and cannot start or end with an underscore or space. The API treats spaces and underscores as equivalent in column names.

One-to-One vs One-to-Many

The multiRow flag on a datatable controls the cardinality:

  • multiRow: false (default) - one-to-one. Only one entry is allowed per linked entity. Suitable for adding a fixed set of extra fields to each client or loan.
  • multiRow: true - one-to-many. Multiple entries are allowed per entity. Suitable for repeating structures like family members, collateral items, or document log entries.

API Reference

All datatable endpoints are under /fineract-provider/api/v1/datatables. Authentication uses HTTP Basic or Bearer token, and the Fineract-Platform-TenantId header is required on all requests.

Create a Datatable

POST /v1/datatables

Creates the physical table in the database, registers it against the specified application table, and makes it available through the data entry API immediately.

bash
curl -k -X POST \
  https://localhost:8443/fineract-provider/api/v1/datatables \
  -H "Fineract-Platform-TenantId: default" \
  -H "Authorization: Basic $(echo -n 'mifos:password' | base64)" \
  -H "Content-Type: application/json" \
  -d '{
    "datatableName": "extra_client_details",
    "apptableName": "m_client",
    "multiRow": false,
    "columns": [
      { "name": "BusinessDescription", "type": "String",  "length": 100, "mandatory": false },
      { "name": "YearsinBusiness",     "type": "Number",  "mandatory": false },
      { "name": "HighestRatePaid",     "type": "Decimal", "mandatory": false },
      { "name": "NextVisit",           "type": "Date",    "mandatory": false },
      { "name": "Gender_cd",           "type": "Dropdown","code": "Gender", "mandatory": false }
    ]
  }'

Response:

json
{ "resourceIdentifier": "extra_client_details" }

The Dropdown column above creates a field called Gender_cd_Gender in the table, linked to the Gender code value list in Fineract.

Column field summary

FieldRequiredDescription
nameYesColumn name. Alphanumeric, underscores, spaces allowed
typeYesOne of the column types listed above
codeRequired if type = DropdownName of the Fineract code value list
lengthRequired if type = StringMaximum character length
mandatoryNo, defaults to falseWhether a value is required on every entry
uniqueNo, defaults to falseAdd a unique constraint
indexedNo, defaults to falseAdd a database index on this column

List Datatables

GET /v1/datatables

Returns all registered datatables, optionally filtered by application table.

bash
# All datatables
curl -k -H "Fineract-Platform-TenantId: default" \
  -H "Authorization: Basic $(echo -n 'mifos:password' | base64)" \
  https://localhost:8443/fineract-provider/api/v1/datatables

# Only datatables linked to m_client
curl -k -H "Fineract-Platform-TenantId: default" \
  -H "Authorization: Basic $(echo -n 'mifos:password' | base64)" \
  "https://localhost:8443/fineract-provider/api/v1/datatables?apptable=m_client"

Retrieve Datatable Details

GET /v1/datatables/{datatableName}

Returns the column definitions and the registered application table for a specific datatable.

bash
curl -k -H "Fineract-Platform-TenantId: default" \
  -H "Authorization: Basic $(echo -n 'mifos:password' | base64)" \
  https://localhost:8443/fineract-provider/api/v1/datatables/extra_client_details

Update a Datatable

PUT /v1/datatables/{datatableName}

Adds, removes, or renames columns. Columns cannot have their type changed - drop and re-add instead.

bash
curl -k -X PUT \
  https://localhost:8443/fineract-provider/api/v1/datatables/extra_client_details \
  -H "Fineract-Platform-TenantId: default" \
  -H "Authorization: Basic $(echo -n 'mifos:password' | base64)" \
  -H "Content-Type: application/json" \
  -d '{
    "addColumns": [
      { "name": "Comment", "type": "Text", "mandatory": false }
    ],
    "dropColumns": [
      { "name": "HighestRatePaid" }
    ],
    "changeColumns": [
      { "name": "BusinessDescription", "newName": "Business_Description", "mandatory": true }
    ]
  }'

Passing apptableName in a PUT request deregisters and re-registers the datatable against the new application table.

Delete a Datatable

DELETE /v1/datatables/{datatableName}

Drops the physical table from the database and removes the registration. This is irreversible.

bash
curl -k -X DELETE \
  https://localhost:8443/fineract-provider/api/v1/datatables/extra_client_details \
  -H "Fineract-Platform-TenantId: default" \
  -H "Authorization: Basic $(echo -n 'mifos:password' | base64)"

Register an Existing Table

POST /v1/datatables/register/{datatable}/{apptable}

Registers a pre-existing database table with Fineract. Use this when you have already created the table via DDL and want to expose it through the Fineract API.

bash
curl -k -X POST \
  https://localhost:8443/fineract-provider/api/v1/datatables/register/existing_table/m_loan \
  -H "Fineract-Platform-TenantId: default" \
  -H "Authorization: Basic $(echo -n 'mifos:password' | base64)"

Deregister a Datatable

POST /v1/datatables/deregister/{datatableName}

Removes the registration without dropping the underlying table. The table remains in the database but is no longer accessible through the API.

bash
curl -k -X POST \
  https://localhost:8443/fineract-provider/api/v1/datatables/deregister/extra_client_details \
  -H "Fineract-Platform-TenantId: default" \
  -H "Authorization: Basic $(echo -n 'mifos:password' | base64)"

Data Entry API

Create an Entry

POST /v1/datatables/{datatableName}/{apptableId}

Adds a row to the datatable for the entity with id apptableId. For one-to-one datatables, calling this endpoint a second time for the same entity will fail.

bash
curl -k -X POST \
  https://localhost:8443/fineract-provider/api/v1/datatables/extra_client_details/1 \
  -H "Fineract-Platform-TenantId: default" \
  -H "Authorization: Basic $(echo -n 'mifos:password' | base64)" \
  -H "Content-Type: application/json" \
  -d '{
    "BusinessDescription": "Livestock sales",
    "Comment": "First comment made",
    "Education_cv": "Primary",
    "Gender_cd": 6,
    "HighestRatePaid": 8.5,
    "NextVisit": "01 October 2012",
    "YearsinBusiness": 5,
    "dateFormat": "dd MMMM yyyy",
    "locale": "en"
  }'

Date fields require dateFormat and locale in the request body.

Read Entries

GET /v1/datatables/{datatableName}/{apptableId}

Returns the entry (one-to-one) or all entries (one-to-many) for the entity.

bash
# Default response format
curl -k -H "Fineract-Platform-TenantId: default" \
  -H "Authorization: Basic $(echo -n 'mifos:password' | base64)" \
  https://localhost:8443/fineract-provider/api/v1/datatables/extra_client_details/1

# Generic result set format (for tabular display)
curl -k -H "Fineract-Platform-TenantId: default" \
  -H "Authorization: Basic $(echo -n 'mifos:password' | base64)" \
  "https://localhost:8443/fineract-provider/api/v1/datatables/extra_client_details/1?genericResultSet=true"

# With ordering (one-to-many)
curl -k -H "Fineract-Platform-TenantId: default" \
  -H "Authorization: Basic $(echo -n 'mifos:password' | base64)" \
  "https://localhost:8443/fineract-provider/api/v1/datatables/extra_family_details/1?order=\`Date of Birth\` desc"

The genericResultSet=true query parameter returns a column-header/rows structure optimised for rendering data tables in UIs.

Read a Specific Entry (One-to-Many)

GET /v1/datatables/{datatableName}/{apptableId}/{datatableId}

Returns a single row from a one-to-many datatable by its row id.

bash
curl -k -H "Fineract-Platform-TenantId: default" \
  -H "Authorization: Basic $(echo -n 'mifos:password' | base64)" \
  https://localhost:8443/fineract-provider/api/v1/datatables/extra_family_details/1/42

Update an Entry (One-to-One)

PUT /v1/datatables/{datatableName}/{apptableId}

bash
curl -k -X PUT \
  https://localhost:8443/fineract-provider/api/v1/datatables/extra_client_details/1 \
  -H "Fineract-Platform-TenantId: default" \
  -H "Authorization: Basic $(echo -n 'mifos:password' | base64)" \
  -H "Content-Type: application/json" \
  -d '{ "YearsinBusiness": 7 }'

Update an Entry (One-to-Many)

PUT /v1/datatables/{datatableName}/{apptableId}/{datatableId}

bash
curl -k -X PUT \
  https://localhost:8443/fineract-provider/api/v1/datatables/extra_family_details/1/42 \
  -H "Fineract-Platform-TenantId: default" \
  -H "Authorization: Basic $(echo -n 'mifos:password' | base64)" \
  -H "Content-Type: application/json" \
  -d '{ "Relationship": "Spouse" }'

Delete Entries

DELETE /v1/datatables/{datatableName}/{apptableId}

Deletes all entries for the entity (one-to-one: the single row; one-to-many: all rows).

bash
curl -k -X DELETE \
  https://localhost:8443/fineract-provider/api/v1/datatables/extra_client_details/1 \
  -H "Fineract-Platform-TenantId: default" \
  -H "Authorization: Basic $(echo -n 'mifos:password' | base64)"

DELETE /v1/datatables/{datatableName}/{apptableId}/{datatableId}

Deletes a specific row from a one-to-many datatable.

Query API

For scenarios where you need to search or filter datatable entries directly, rather than fetching by entity id, Fineract provides two query endpoints.

Simple Query

GET /v1/datatables/{datatableName}/query

bash
curl -k -H "Fineract-Platform-TenantId: default" \
  -H "Authorization: Basic $(echo -n 'mifos:password' | base64)" \
  "https://localhost:8443/fineract-provider/api/v1/datatables/extra_client_details/query?columnFilter=YearsinBusiness&valueFilter=5&resultColumns=BusinessDescription,YearsinBusiness"

Parameters:

  • columnFilter - column name to filter on
  • valueFilter - value to match
  • resultColumns - comma-separated list of columns to return

Advanced Paged Query

POST /v1/datatables/{datatableName}/query

Accepts a paged query body for more complex filtering and pagination:

bash
curl -k -X POST \
  https://localhost:8443/fineract-provider/api/v1/datatables/extra_client_details/query \
  -H "Fineract-Platform-TenantId: default" \
  -H "Authorization: Basic $(echo -n 'mifos:password' | base64)" \
  -H "Content-Type: application/json" \
  -d '{
    "page": 0,
    "size": 20,
    "request": {
      "resultColumns": ["BusinessDescription", "YearsinBusiness"]
    }
  }'

Returns a paged Page<JsonObject> response.

Business Events

Fineract fires business events for datatable data entry operations. These events are available through the business events system (Kafka or ActiveMQ, depending on your configuration):

EventFired when
DatatableEntryCreatedBusinessEventA new row is added to any datatable
DatatableEntryUpdatedBusinessEventAn existing row is updated
DatatableEntryDeletedBusinessEventA row is deleted

See the Business Events & Kafka Integration Guide for configuration details.

Permissions

Datatables use Fineract's standard permission system. The platform automatically creates read and write permissions for each registered datatable under the DATATABLE permission group. Users with the READ_<datatableName> and WRITE_<datatableName> permissions can access entries for that table.

Finecko and Datatables

Datatables created and managed through the API are preserved across Finecko upgrades. No additional configuration is required - custom data structures survive the upgrade process without intervention.

Learn more about Finecko managed upgrades →