Appearance
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:
apptableName | Entity |
|---|---|
m_client | Clients |
m_group | Groups |
m_loan | Loans |
m_office | Offices |
m_saving_account | Savings accounts |
m_product_loan | Loan products |
m_savings_product | Savings products |
Column Types
Each column in a datatable must have one of the following types:
| Type | Notes |
|---|---|
Boolean | True/false value |
Date | Date only, no time component |
DateTime | Date and time |
Decimal | Floating-point numeric |
Dropdown | Foreign key into a Fineract code value list. Requires code field (mandatory). Column name becomes code_cd_<name> |
Number | Integer numeric |
String | Variable-length text. Requires length field (mandatory) |
Text | Unlimited 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
| Field | Required | Description |
|---|---|---|
name | Yes | Column name. Alphanumeric, underscores, spaces allowed |
type | Yes | One of the column types listed above |
code | Required if type = Dropdown | Name of the Fineract code value list |
length | Required if type = String | Maximum character length |
mandatory | No, defaults to false | Whether a value is required on every entry |
unique | No, defaults to false | Add a unique constraint |
indexed | No, defaults to false | Add 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_detailsUpdate 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/42Update 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 onvalueFilter- value to matchresultColumns- 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):
| Event | Fired when |
|---|---|
DatatableEntryCreatedBusinessEvent | A new row is added to any datatable |
DatatableEntryUpdatedBusinessEvent | An existing row is updated |
DatatableEntryDeletedBusinessEvent | A 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.