Customizing Apache Fineract business events and Avro schemas: when your downstream systems need more than the default payload
An integration team consuming Fineract events from Kafka or ActiveMQ often finds that the default event does not carry the fields its downstream systems need. The fix is usually a custom event or an enriched version of an existing one, defined as a BusinessEvent class with a matching Avro schema, rather than a change to the core sources. External business events are messages Fineract publishes when something happens in the platform, such as a client being created. Publishing is disabled by default, so the first step is confirming which event types your deployment actually emits and to which broker. The project's README uses ClientCreated as its example of an external business event, and it can be sent to ActiveMQ or other JMS brokers, or to Apache Kafka.
The first design decision is whether you are adding a new event or enriching an existing one. A new event suits a business occurrence that Fineract does not publish today, and it gives you a clean schema that only your consumers depend on. Enriching an existing event is the harder path: consumers already rely on its current fields, so any change to its Avro schema must stay compatible with them, and a higher-priority custom serializer for that event replaces the default payload for everyone reading the topic. Before writing code, list every consumer of the topic, the fields each one reads, and whether any of them parse monetary values, since Avro bigdecimal fields need the same precision and scale agreed on both sides.
Confirm the following with your own environment before the build starts: the Fineract version you run and whether its event schemas come from the fineract-avro-schemas module you will build against, the broker and topic naming your consumers expect, and who owns the schema registry or versioning policy. Test with a non-production tenant and a consumer that reads the topic exactly as production does, because a serializer that loads cleanly can still break a consumer that expects an older field order or type. Plan for schema evolution as an ongoing cost, since each Fineract upgrade may change the event definitions your custom serializers depend on. The Apache Fineract platform documentation is the next read for the event configuration settings.
Which customization path fits your event change
Use this path when Fineract publishes nothing for the domain action you need, such as an outcome produced by logic in your own custom module. You add a new BusinessEvent implementation, define its payload, and let the existing external publishing path send it to your broker. Existing consumers keep working because they never subscribe to the new event, but no current consumer receives it either until someone builds one.
- Code artifacts: a new BusinessEvent class in your custom module, its payload type, and the wiring that makes the publisher pick it up.
- Schema artifacts: a new Avro schema with its own name, kept separate from the schemas Fineract already defines.
- Consumer impact: none on existing consumers, but each new consumer must be built, tested and documented against the new contract.
- Upgrade risk: low for the event itself, although the custom module must be rebuilt and retested with each Fineract release.
Confirm that external event publishing is enabled in your configuration, since it is disabled by default, and check whether an existing event with added fields could carry the same meaning before you create a new event name.
Use this path when consumers already receive an event such as ClientCreated and need additional attributes. The change touches the Avro schema for that event in fineract-avro-schemas, along with the code that maps domain data into the event. Most of the risk sits in the schema, because Avro readers depend on compatibility between the writer's schema and the reader's schema.
- Code artifacts: the mapping code that fills the event from domain objects, and the BusinessEvent class that carries the data.
- Schema artifacts: the Avro schema in fineract-avro-schemas. New fields need default values so that older messages and older readers still decode.
- Money fields: use the bigdecimal logical type for amounts so precision and scale are explicit and floating-point rounding does not creep into payment data.
- Consumer impact: consumers on the previous schema must ignore unknown fields, so test both old and new readers against the same messages.
- Upgrade risk: a later Fineract release may change the same schema, so keep your additions small, document them, and re-verify compatibility after each upgrade.
Confirm the schema compatibility mode that your broker and consumers enforce, and agree on the field names and defaults with every consumer team before the first message is published.
Use this path when consumers must keep the event shape they already expect, but the encoding or a field transformation has to change, for example to mask a value or to produce a different representation. You register a custom serializer for the existing event with a higher priority than the default, so Fineract selects yours. This path gives the most control, and it also couples your code most tightly to Fineract internals.
- Code artifacts: a serializer implementation for the specific event type and the registration that gives it higher priority than the default.
- Schema artifacts: the output must match a schema your consumers already use. The default Avro schema for that event can remain in the repository while your serializer handles that event.
- Consumer impact: the field set looks unchanged, but differences in encoding, null handling or field order can break consumers without an obvious error, so contract tests are essential.
- Upgrade risk: the highest of the three paths, because a new release can change the default serializer, the event class or the priority rules your code depends on.
- Ownership: someone must own the serializer after go-live, including tests that run against each Fineract upgrade.
Confirm how serializer priority is resolved, and how the default serializer behaves, in the exact Fineract release you run by reading the project source and running consumer contract tests.
Configuration options and when each fits
This table compares the main choices for publishing and enriching Fineract business events, from turning on external publishing to registering a custom serializer. Read each row for the situation it suits, the trade-off it carries, and what your team should confirm in its own environment before committing to it.
| Option | Fit and trade-off | What to confirm |
|---|---|---|
| Enable external event publishing | Required before any external consumer sees events. It is disabled by default, so every downstream system depends on this switch being set deliberately. Trade-off: more moving parts in each environment. | Which environments enable it, and which events the consumers actually need. |
| ActiveMQ or other JMS broker | Fits sites that already run a JMS broker and have consumers built on it. Trade-off: your event contract and operational support follow the broker you choose. | Broker version and support status, and how your consumers subscribe and acknowledge messages. |
| Apache Kafka | Fits sites with a Kafka platform, replay needs or multiple independent consumers. Trade-off: topic, partition and retention design becomes your responsibility. | Topic naming, partitioning key, retention and access control with your Kafka team. |
| Add a new BusinessEvent class | Fits a domain event Fineract does not publish today, such as an institution-specific workflow outcome. Trade-off: you own the event's lifecycle and must keep it stable across upgrades. | Where the trigger lives in the code path, and whether the event is needed by more than one consumer. |
| Extend an existing Avro schema in fineract-avro-schemas | Fits adding fields to an event consumers already read. Trade-off: schema changes affect every consumer, and compatibility rules decide whether they break. | Schema compatibility mode, consumer tolerance for unknown fields, and the schema registry in use. |
| Use bigdecimal fields for monetary values | Fits amounts, balances and rates, where exact decimal values matter for reconciliation. Trade-off: consumers must handle decimal encoding and precision correctly. | Scale and precision per currency, and how each consumer parses and stores the value. |
| Register a higher-priority custom serializer | Fits cases where a field needs different formatting or enrichment for one consumer. Trade-off: it overrides default behavior, so it can change output for every event of that type. | Priority ordering against other serializers, and regression tests covering the default output. |
Setup sequence for a custom event and Avro schema
This sequence takes a planned event from a confirmed Fineract release and a test environment through schema design, serializer code, image build and broker verification, so each change is checked before it reaches production. It produces a tested event contract that consumers can read.
Confirm the release and enable event publishing
The platform engineering lead confirms the exact Fineract release the deployment runs, since the event classes and Avro schemas must match that version. In a non-production environment, enable external event publishing to the broker you plan to use, either ActiveMQ or other JMS brokers or Apache Kafka, because external publishing is disabled by default. Done when a stock event such as ClientCreated appears on the broker in the test environment, which proves the publishing path works before any customization is added.
Define the consumer contract
Before touching code, the integration owner and each downstream consumer agree on the event name, the fields they need, field types and units, which fields are optional, and how the event will change over time. For money values, record the currency and scale expectations so the bigdecimal representation is understood by every reader. Done when the contract is written down, reviewed by every consuming team, and linked to the ticket that will carry the change.
Add or change the Avro schema
The developer adds or modifies the Avro schema in the fineract-avro-schemas module, following the structure the existing schemas use. New fields should be optional or carry defaults so that consumers built against earlier versions keep working. Run the schema build and confirm the generated classes compile. Done when the schema builds cleanly and the diff shows only the intended fields, with no changes to existing field names or types.
Implement the BusinessEvent or custom serializer
For a new event, the developer writes a BusinessEvent class that carries the data and maps it to the Avro record. To enrich an existing event, the developer writes a serializer with higher priority than the default so that it replaces the standard mapping for that event only. Keep the logic in a custom module so it stays separate from core sources, and confirm the serializer's priority against the existing one in the code. Done when unit tests show the expected Avro payload for sample inputs, including a null optional field and a bigdecimal amount.
Build the image with the custom module
The platform engineer packages the custom module into the Fineract image and builds it with the same pipeline used for other deployments. Start the build in the test environment and confirm the service starts, runs its database migrations on the write instance, and registers the custom serializer. Done when the image starts successfully, the logs show the custom serializer loaded, and a triggering action produces an event without errors.
Verify messages with a schema-aware consumer
Trigger the business action in the test environment and read the message from the broker with a consumer that uses the same Avro schema version. Check that every field is decoded, that money values keep their expected precision, that optional fields behave as the contract states, and that the existing default event is still produced where expected. Repeat the check against a message produced by the previous image to confirm backward compatibility. Done when the consumer decodes both messages and the results are recorded against the contract.
Handling bigdecimal fields and schema evolution safely
Monetary amounts in events must be represented exactly, because binary floating-point types cannot store most decimal fractions without rounding error, and that error accumulates when consumers sum, reconcile or compare balances. Avro's bytes type with the decimal logical type, which Fineract's custom serializers can emit as bigdecimal fields, carries a fixed scale and precision in the schema itself. The decision that matters is not the Java type but the agreement on precision, scale, rounding mode and currency handling. Consumers in your ledger, data platform and reporting tools must read the same values, so write those parameters into the schema documentation and into the consumer contract before the first event is published. Confirm with your accounting team which scale covers every currency your products use, including currencies with more than two minor units.
Once consumers depend on a schema, every change is a compatibility decision. Adding a field with a default value is usually backward compatible for readers using the new schema, while removing a field, renaming it or changing a type can break consumers that still expect the old shape. Changing a decimal field's scale is a breaking change even when the Avro type name looks the same. Choose one Avro compatibility mode for each subject, such as backward, forward or full, and confirm it with the owners of every consumer and with the schema registry you operate. Check the registry's own documentation for the exact modes it supports and how it enforces them, since the behavior differs between registry implementations.
Write a consumer contract test for each subscriber. The test should deserialize a set of sample events produced from the current schema, including edge values such as zero, the largest expected amount and amounts with the maximum scale, and assert the exact decimal result. Run the same test against the proposed new schema in your build pipeline and block the change if any consumer fails. Keep the sample events under version control with the schema, and record which consumer team owns each contract so that a failing test has a named owner. Ask your DeployOpen contact to include this contract suite in the test plan for any custom event so that it is maintained through upgrades.
Security and operational risks in custom event payloads
Custom events leave Fineract and travel to brokers and consumers you may not fully control, so each payload, topic and serializer is a risk surface. The cards below pair each risk with a control the team can set and verify before events reach production.
Personal data in event payloads
A new event or enriched field can carry client names, identifiers, account details or KYC-related values to every subscriber on the topic. Review each field against your data minimization rules before it enters the Avro schema, and decide which consumers are allowed to receive it. Where a consumer only needs an identifier, publish the identifier and let the consumer look up the rest through an authorized API call. Document the field list for each event so privacy and audit teams can review it.
Broker authentication and topic access
Events published to ActiveMQ, other JMS brokers or Apache Kafka are only as protected as the broker's authentication and topic authorization. Give the Fineract publisher its own credentials, grant it write access only to its event topics, and give each consumer read access to the specific topics it needs. Confirm how the broker connection is secured in transit and where its credentials are stored, since a leaked publisher credential can inject false business events.
Tenant identification in events
Fineract is multi-tenant, so a consumer receiving events from several tenants must be able to tell which tenant produced each one. Check that every custom event carries the tenant identifier as a field, not only in the topic name or the message key. Test this with two tenants publishing the same event type and confirm that a consumer filtering by tenant never receives the other tenant's records.
Custom serializer overriding a core serializer
A higher-priority custom serializer for an existing event replaces the core behavior for every consumer, not only the one you had in mind. A change in field names, decimal handling or nullability can break downstream parsers without any error in Fineract. Keep a written record of each override, compare its output with the core serializer in tests, and review the override whenever you upgrade Fineract.
Event loss and duplication during outages
When the broker is unavailable, events may be lost or published more than once depending on how publishing failures are handled and how the consumer acknowledges messages. Ask the team to confirm the delivery guarantees of the broker and of the Fineract publishing path during a broker restart. Design consumers to tolerate duplicates by keying on a stable event identifier, and test a broker outage in a non-production environment before relying on the stream for reconciliation.
Monitoring of publish failures
A publish failure that goes unnoticed can leave downstream ledgers, reports or notifications silently out of step with Fineract. Alert on failed publishes, growing consumer lag and schema validation errors, and route those alerts to the team that owns the integration. Set a regular reconciliation between a count of business transactions in Fineract and the count of events each consumer has received, so drift is found early.
Questions teams ask before changing Fineract events
How much effort does a custom Fineract event take to build?
Effort depends mostly on how many events change, how many fields each event carries, and whether the event is new or an enrichment of one Fineract already publishes. A new BusinessEvent class needs a payload definition, an Avro schema in the fineract-avro-schemas module, a serializer path, and tests on both the producing and consuming side. Enriching an existing event adds a custom serializer with higher priority, which is smaller work but still needs the same schema and consumer changes. Ask any partner to break the quote down by event, by schema, by broker configuration and by test coverage, so you can see which piece drives the total.
Can our own developers build custom Fineract events without outside help?
A team with strong Java and Spring experience can build a first custom event, provided it has time to learn the Fineract codebase, its build, and its test setup. The harder part is usually not the Java code but agreeing the schema with every consumer and keeping it stable afterward. Teams with no Avro experience, no Kafka or JMS operations experience, or no spare capacity for upgrade testing often find an outside partner shortens the first release. Whichever path you choose, decide who owns the schema before the code starts.
What skills and inputs does a custom event need from our side?
You need Java developers who can work in a Spring-based Fineract build, someone who can operate the broker (Kafka or an ActiveMQ or other JMS broker), and a named owner for Avro schemas. You also need the consumer owners: the teams whose systems read the events must confirm field names, types and nullability, and agree how they handle a new field they do not yet use. Expect to provide a sample environment, test accounts with realistic loan and savings data, and access to broker topics in non-production first. Missing consumer input is the most common reason these projects stall.
How do Fineract upgrades affect custom serializers and schemas?
Every upgrade can change the events Fineract publishes, the fields inside them, or the classes your serializer depends on, so custom serializers need to be tested against each new release before it reaches production. Check the release notes and the event classes in the target version, then rerun your payload tests and consumer contract checks. Keep schema changes additive where possible and version them explicitly, so existing consumers keep working while they migrate. Fineract releases are created quarterly, so build the upgrade check into your normal cycle rather than treating it as a one-off.
Should we publish Fineract events to Kafka or ActiveMQ?
Choose the broker based on what your consumers already run and operate, not on the event code itself. Fineract can publish external business events to ActiveMQ or other JMS brokers and to Apache Kafka, and the feature is disabled by default, so you turn it on deliberately. Kafka suits teams that need durable, replayable streams shared by many consumers. JMS suits organizations already standardized on a JMS broker. Confirm retention, access control, monitoring and disaster recovery for whichever broker you pick, because those costs recur after launch.
How should we test custom event payloads before production?
Test at three levels: the serializer must produce the expected Avro payload for each business event, including decimal values and null fields; the schema must validate against the consumer contract; and an end-to-end run must publish the event from a real Fineract instance to a non-production broker and confirm a consumer reads it correctly. Use test data that covers loans, savings and accounting edge cases, and check money fields closely, since bigdecimal values lose meaning if scale or rounding changes silently. Keep these tests in the same regression pipeline as the rest of your customizations so upgrades trigger them automatically.
What should a quote for custom Fineract events include?
A useful quote lists each event in scope, whether it is new or enriched, the fields and their Avro types, the broker and environments involved, and the consumers that must be tested. It should separate one-time work (design, build, schema agreement, initial tests) from recurring work (upgrade testing against each Fineract release, schema changes, broker operations if DeployOpen runs them). Ask for the assumptions about your access to Fineract environments, broker topics and consumer teams, plus the acceptance criteria. DeployOpen works from a written scope, so the quote will follow from that document rather than from a standard price.
Where to confirm event and build details
Confirm every class name, package path, schema file and configuration key against the release you actually run before you write any code. The authoritative sources are the Apache Fineract platform documentation and the apache/fineract repository on GitHub. Start with the Apache Fineract platform documentation for the supported way to enable external events, then open the repository to find the current event classes, the BusinessEvent serializer code and the fineract-avro-schemas module. Names and locations can change between releases, so treat a path copied from a blog post, an older guide or a forum answer as a lead to verify, not as a fact.
Pin your check to the version in production. The Fineract website lists 1.15.0 as the current stable release, and the README states that official releases are created quarterly and that end-of-life releases are archived. If you run an older 1.x release, read the tag or branch for that version in the repository rather than the main branch, because the event classes, Avro schema fields and serializer ordering may differ. Record the release tag, commit and the exact schema file names in your design document so that later upgrades can be compared against a known baseline.
Use the README section on external business events as the reference for the publishing setup. It explains that external publishing to ActiveMQ or other JMS brokers and to Apache Kafka is disabled by default, which means your custom event work will have no effect until the feature is switched on in your environment. Confirm the relevant configuration keys and topic or queue settings for your release, and test them in a non-production environment that mirrors your broker setup. Ask your Kafka or ActiveMQ administrators to confirm the topic naming and schema registry arrangements before the custom serializer is finalized.
How DeployOpen works on this
DeployOpen plans, builds, tests and documents custom Fineract business events and Avro schemas inside your own infrastructure, without forking Fineract. Each engagement starts from a written scope that names the events, consumers and schema changes involved, and the work is handed over with the code, tests and runbooks your team needs to operate it.
How we scope
- We list each event to add or enrich, its trigger and the consumers that will read it.
- We map each field to a source value and confirm which fields need bigdecimal handling.
- Engagements are fixed-scope, with deliverables and timelines agreed in writing before work starts.
How we build
- We write new BusinessEvent classes and custom serializers as a module outside core Fineract sources.
- We add or extend Avro schemas in fineract-avro-schemas and document every compatibility decision.
- We test each event against a running backend, including payload content and consumer parsing.
How we hand over
- You receive source code, schema files, test suites and a record of the configuration used for each topic.
- We document how to enable external publishing to ActiveMQ or Kafka and how to verify the output.
- We walk your engineers through the event catalog, schema versioning rules and upgrade checks.
How we support
- Ongoing support, upgrades and continuous testing are available as a retainer after handover.
- Retainers can cover re-testing of custom serializers when you move to a new Fineract release.
- Ask any provider to confirm how they will handle schema changes that consumers have not yet adopted.

