When 40% of a Bank's CTR Batch Came Back Rejected: A goAML Recovery Story
A Kenyan bank was losing days to rejected goAML CTR batches. The fix was not more reviewers — it was validating XML against the FRC schema before submission.

Composite scenario drawn from typical East African deployments. Institution details are anonymised and figures are representative rather than attributable to a single client.
The month the reports stopped going through
The compliance manager at a tier-two Kenyan commercial bank had a routine she disliked. On the 3rd of every month, her team exported the previous month's cash transactions from the core banking system into a spreadsheet, cleaned them by hand, ran a macro that produced goAML XML, and uploaded the file to the Financial Reporting Centre portal.
Then they waited.
In a good month, the portal accepted the batch and the team moved on. In a bad month — and bad months were becoming normal — the portal returned a validation error referencing an XSD element nobody in the building could interpret. One month, roughly 40% of the batch bounced. The team had four working days to resubmit before the reporting deadline, and no reliable way to tell which of the several thousand records were at fault.
This is the part of AML compliance that rarely appears in policy documents. The obligation is clear. The controls exist. The reports still do not arrive on time.
What was actually breaking
When we walked the process with the team, the failures clustered into four causes, none of which were about compliance judgement:
- Silent schema drift. The macro had been written against an earlier goAML XSD. When the schema moved to v5.0.2, several optional elements became mandatory and one enumeration changed. Nobody had rebuilt the macro.
- Free-text identifier fields. Customer ID types were entered inconsistently in core banking — "National ID", "NATIONAL_ID", "ID Card" — and the mapping layer passed them straight through.
- Threshold logic living in a spreadsheet. Cash transaction detection depended on a filter that one analyst maintained. Aggregation across same-day, same-customer transactions was done by eye.
- No pre-flight check. The first time anyone knew a file was invalid was after the regulator told them.
The last point is the important one. Every other problem was survivable if it was caught before submission. None of them were survivable when the feedback loop ran through the FRC portal with a four-day deadline attached.
What changed
The bank deployed Creodata goAML Reporting as a single-tenant Azure instance alongside its existing core banking platform. Three things moved:
Mapping moved out of spreadsheets. Transaction and customer data flows in through a secure integration and is mapped automatically to the goAML data model. Identifier types are normalised against the schema's enumerations at ingest, so "ID Card" never reaches the XML generator.
Detection moved into the platform. Cash transaction thresholds — including same-day aggregation rules — are configured once and applied consistently. Transactions that meet the threshold surface automatically instead of depending on an analyst's filter.
Validation moved before submission. Every generated file is validated against the live goAML XSD v5.0.2 inside the platform. Records that fail are flagged with the specific element and the specific record, in language a compliance analyst can act on, days before the file goes anywhere near the regulator.
The STR side was handled separately. Suspicious transaction reports now get drafted in a dedicated workspace rather than in Word, so the narrative, the linked transactions and the supporting entities travel together and can be reconstructed later.
The result
The first live submission went out six weeks after kickoff. It was accepted on the first attempt, and so were the ones after it. The 3rd of the month stopped being an event.
Two secondary effects mattered more than the team expected:
Examination preparation collapsed from weeks to hours. The platform keeps an immutable audit trail of what was reported, when, by whom, and against which source records. When the supervisor asked how a specific threshold decision had been made eight months earlier, the answer was a query rather than an archaeology project.
The compliance team stopped doing data entry. The hours previously spent reconciling exports went into what the team is actually paid for — reviewing alerts and writing defensible suspicion narratives. Across a typical month, that is upward of 80 hours redirected from format-wrangling to analysis.
What we would tell the next bank
Three things generalise from this engagement.
Rejections are a symptom, not the disease. Institutions tend to respond to a rejected batch by adding a reviewer. That adds cost and catches perhaps half the errors, because humans are poor at spotting XSD violations in XML. The durable fix is a machine that checks the file against the schema the regulator actually uses.
Schema versions change and nobody sends you a memo. If your XML generation lives in a macro, a script or a vendor module that is not tracked against the published XSD, assume it will silently fall out of compliance. Validation against the live schema is the only reliable defence.
Country matters. The FRC in Kenya, the FIU in Tanzania, the FIA in Uganda, the FIC in Rwanda and the FIC in Zambia each layer local requirements over the goAML baseline. A platform that treats "goAML" as one target will fail at the border. Multi-jurisdiction institutions should be testing country-specific rules explicitly, not assuming portability.
The bank in this story now files in two jurisdictions from one deployment. The compliance manager still dislikes the 3rd of the month, but for ordinary reasons.
Further reading: The Complete goAML Reporting Guide · goAML Submission Rejected? How to Diagnose and Fix Common Errors
Facing rejected goAML batches or preparing for a first submission? Book a consultation or explore goAML Reporting to see how schema validation works before your file reaches the regulator.