goAML XSD v5 XML Generation: The Complete Technical Guide
If you have ever attempted to build a goAML XML report from scratch, you already know that the process is substantially more complicated than it first appears. The UNODC goAML schema version 5.0.2 defines over 200 elements, many with strict enumeration constraints, all arranged in a deeply nested hierarchy with different mandatory element sets depending on whether you are generating a Cash Transaction Report or a Suspicious Transaction Report. Country-level FIUs then layer additional validation requirements on top of the base schema — and those requirements change.
Most first attempts at goAML XML generation fail at submission. The report looks correct in a text editor. The XML is well-formed. But the FIU portal returns a validation error for a field that is three levels deep in the party hierarchy, formatted correctly for every other country except this one. The developer makes a fix, regenerates, resubmits, and encounters a different error. The iteration cycle can take days.
This guide is written for IT managers, developers, and technical architects responsible for building or evaluating goAML XML generation capabilities. It covers the schema structure, common failure points, country-specific extensions, and the architecture of a robust XML generation pipeline.
goAML XML Schema v5.0.2 Overview
History of the goAML Schema
UNODC's goAML software has undergone several major schema revisions since its initial deployment in the mid-2000s. Version 3 of the schema was the dominant version across early adopters and is still encountered in legacy FIU deployments. Version 4 introduced significant structural changes to transaction and party elements, improving support for complex ownership structures and cross-border transactions.
Version 5.0.2 — the current version deployed across all five ESAAMLG member country FIUs — introduced further structural refinements, expanded enumeration sets for transaction types and identification document types, and formalised the extension mechanism that country FIUs use to impose additional validation requirements beyond the base UNODC schema.
Critically, when UNODC releases an XSD update, it is not always a backward-compatible change. Code that generated valid v4 XML may generate invalid v5 XML on specific element combinations. Any XML generation implementation must be pinned to the exact schema version deployed by the target FIU and updated when that FIU migrates.
Schema Structure: Report → Transaction → Party → Account → ID Document → Address
The goAML XML document hierarchy follows a logical structure that mirrors the real-world financial intelligence report:
Report is the root element. It contains the reporting entity identification, submission metadata, the reporting person's details, and one or more transaction records.
Transaction elements sit immediately under the Report. Each transaction has a type, amount, currency, date, and description. A single report can contain multiple transactions — common in CTR reports where a customer has made multiple cash transactions in the reporting period.
Party elements describe the individuals or legal entities involved in a transaction. Parties are linked to transactions via from_person, to_person, from_entity, and to_entity elements, each of which contains the nested party record. A party that appears in multiple transactions can be referenced by ID rather than fully repeated in each transaction.
Account elements capture the financial accounts involved — bank account numbers, type codes, and the financial institution holding the account. Accounts are linked to their respective transaction side (from_account, to_account).
ID Document elements sit inside person records and capture national identity, passport, driving licence, and other identity verification documents. National ID is mandatory for Kenyan persons under FRC validation rules.
Address elements capture physical or postal addresses for persons and entities. Address validation rules vary by country — some FIUs require street-level detail; others accept country-only entries for non-resident parties.
STR vs. CTR Schema Differences
While the root schema structure is shared between Suspicious Transaction Reports and Cash Transaction Reports, there are meaningful differences in which elements are mandatory, optional, or prohibited:
CTR reports require transaction_amount with exact decimal precision, CASH as the transaction type, and full account detail on both sides of the transaction. The CTR threshold amount and the currency code must be precisely consistent with the country profile's declared threshold. Multiple cash transactions in the same reporting period may be consolidated into a single CTR.
STR reports require the is_suspicious flag set to true, a non-empty reason element describing why the transaction is suspicious, narrative text of meaningful length and substance, at least one indicator code referencing a known money laundering typology from the FIU's indicator list, and reporting_person detail. The transaction elements in an STR may include non-cash transactions and structured patterns that individually fall below CTR thresholds.
Downloading the Official XSD from UNODC
UNODC makes the goAML XSD schema files available through its official goAML documentation portal. The canonical source for the XSD files, release notes, and validation guidance is the UNODC goAML product page. Each country FIU's portal documentation should also be consulted, as FIUs publish country-specific schema extensions and validation rule documents alongside the base UNODC schema.
It is strongly recommended to download the XSD directly from the FIU portal for your target country rather than using a third-party source, as country extensions modify the base schema in ways that are not always captured in general documentation.
The goAML XML Document Structure
The following is an annotated example of a well-formed goAML v5.0.2 CTR report for a fictional Kenyan reporting entity. All entity and person data is entirely fictitious.
<?xml version="1.0" encoding="UTF-8"?>
<Report xmlns="http://goaml.unodc.org/goaml/en">
<!-- Reporting entity identification — must match FIU registration exactly -->
<rentity_id>KE-FRC-12345</rentity_id>
<rentity_branch>NAIROBI-HQ</rentity_branch>
<!-- Submission metadata -->
<submission_code>E</submission_code> <!-- E = Electronic -->
<report_code>CTR</report_code> <!-- CTR or STR -->
<entity_reference>CTR-2026-0001</entity_reference> <!-- Internal reference -->
<fiu_ref_number></fiu_ref_number> <!-- Blank on first submission; FIU populates on acceptance -->
<submission_date>2026-03-25</submission_date> <!-- YYYY-MM-DD strictly required -->
<currency_code_local>KES</currency_code_local> <!-- ISO 4217 currency code -->
<!-- Reporting person — the compliance officer or designated MLRO -->
<reporting_person>
<gender>M</gender> <!-- M or F — not Male/Female -->
<title>Mr</title>
<first_name>James</first_name>
<last_name>Mwangi</last_name>
<birthdate>1980-05-15</birthdate> <!-- YYYY-MM-DD — not optional for Kenya FRC -->
<id_number>12345678</id_number> <!-- National ID of reporting person -->
<!-- Address and phone elements follow here -->
</reporting_person>
<!-- One or more transaction elements -->
<transaction>
<transactionnumber>TXN-2026-00123</transactionnumber>
<transaction_location>NAIROBI-CBD-BRANCH</transaction_location>
<date_transaction>2026-03-24</date_transaction> <!-- Date of transaction -->
<teller>T001</teller> <!-- Teller/officer ID -->
<currency_amount>
<amount>1250000.00</amount> <!-- Exactly 2 decimal places required -->
<currency_code>KES</currency_code>
</currency_amount>
<!-- Transaction type — must be valid enumeration value from XSD -->
<transaction_type>CASH_DEPOSIT</transaction_type>
<!-- The depositing party (from side) -->
<from_person>
<gender>F</gender>
<title>Ms</title>
<first_name>Wanjiru</first_name>
<last_name>Kamau</last_name>
<birthdate>1985-11-20</birthdate>
<id_number>87654321</id_number>
<id_type>NATIONAL_ID</id_type> <!-- Enumeration — must match XSD allowed values -->
<address>
<address_type>HOME</address_type>
<city>Nairobi</city>
<country>KE</country> <!-- ISO 3166-1 alpha-2 -->
</address>
</from_person>
<!-- The receiving account (to side) -->
<to_account>
<institution_name>First National Bank Kenya</institution_name>
<institution_code>KE-FRC-12345</institution_code>
<account>
<account_number>1234567890</account_number>
<account_name>Wanjiru Kamau</account_name>
<account_type>SAVINGS</account_type> <!-- Enumeration -->
<currency_code>KES</currency_code>
<opened>2022-06-15</opened>
</account>
</to_account>
</transaction>
</Report>
This example illustrates several critical formatting requirements that are discussed in detail in the sections below. The structure is correct for a basic CTR, but production reports require additional elements for complex transaction patterns, multiple involved parties, and multi-transaction reports.
Mandatory vs. Optional Elements by Report Type
The following table captures the key elements and their requirements across CTR and STR report types. FIU-specific extensions are noted but not exhaustively listed — always consult the current country-specific validation documentation.
| Element | CTR Required | STR Required | Data Type | Validation Rule |
|---|---|---|---|---|
| rentity_id | Yes | Yes | String | Must match FIU-registered entity ID exactly |
| rentity_branch | Yes | Yes | String | Must match registered branch code |
| submission_code | Yes | Yes | Enum | E (Electronic) only for automated submissions |
| report_code | Yes | Yes | Enum | CTR or STR |
| entity_reference | Yes | Yes | String | Unique internal reference; max 50 chars |
| submission_date | Yes | Yes | Date | YYYY-MM-DD format |
| currency_code_local | Yes | Yes | String | ISO 4217 (KES, UGX, TZS, ZMW, RWF) |
| reporting_person | Yes | Yes | Complex | Full name, ID, birthdate mandatory (Kenya) |
| transaction.date_transaction | Yes | Yes | Date | YYYY-MM-DD format |
| transaction.amount | Yes | Yes | Decimal | Exactly 2 decimal places |
| transaction.currency_code | Yes | Yes | String | ISO 4217 |
| transaction.transaction_type | Yes | Yes | Enum | Must match XSD enumeration list |
| from_person or from_entity | Yes | Yes | Complex | At least one from-side party required |
| to_account or to_person | Yes | Conditional | Complex | Required for CTR; conditional for STR |
| is_suspicious | Prohibited | Yes | Boolean | true/false lowercase |
| reason | Prohibited | Yes | String | Non-empty; describes suspicious behaviour |
| narrative | Prohibited | Yes | String | Non-empty; STR narrative text |
| indicator | Prohibited | Yes (min 1) | String | From FIU's published indicator code list |
| id_number | Yes | Yes | String | National ID mandatory for Kenya persons |
| id_type | Yes | Yes | Enum | NATIONAL_ID, PASSPORT, DRIVING_LICENCE, etc. |
| account_number | Yes | Conditional | String | Required on CTR accounts |
| account_type | Yes | Conditional | Enum | SAVINGS, CURRENT, LOAN, etc. |
Common XML Generation Challenges
Encoding Issues: UTF-8, BOM-Free
goAML portal implementations are notoriously strict about character encoding. The XML declaration must specify UTF-8 encoding, and the file must be saved as UTF-8 without a Byte Order Mark (BOM). Windows-centric XML libraries often write a UTF-8 BOM by default, which causes the goAML portal to reject the submission with an encoding error that is frequently misdiagnosed as a schema error.
If your XML is generated on a Windows system using .NET's XmlWriter, ensure you construct the writer with new UTF8Encoding(false) — the false parameter explicitly suppresses BOM output. In Python, use encoding='utf-8' without the utf-8-sig variant. This is one of the most common silent failures in first-generation XML implementations.
Date Format Strictness: YYYY-MM-DD Always
Every date field in the goAML schema requires ISO 8601 format: YYYY-MM-DD. East African core banking systems frequently store and export dates in DD/MM/YYYY format, which is the prevalent human-readable format in the region. A transformation layer must normalise all incoming date data before it enters the XML generation pipeline.
Dates stored as Excel serial numbers (common in CSV exports from some core banking systems) require a different transformation. December 31, 1899 is serial number 1 in Excel's date system; March 25, 2026 is serial number 46104. Any XML generation system consuming Excel-exported transaction data must handle this conversion explicitly.
Decimal Precision: Exactly Two Decimal Places
Amount elements must be formatted with exactly two decimal places. An amount of KES 1,250,000 must appear as 1250000.00, not 1250000, not 1,250,000.00 (no thousands separator), and not 1250000.0. The XSD defines amount elements as xs:decimal with a fractionDigits constraint of 2. Amounts derived from division operations in code must be explicitly rounded and formatted before insertion into the XML.
Boolean Fields: Lowercase true/false
The is_suspicious element and other boolean fields in the goAML schema use XML Schema boolean type, which accepts true and false (lowercase) or 1 and 0. Many developers, particularly those working in languages with case-insensitive boolean representations, inadvertently generate True, False, TRUE, or FALSE — all of which fail schema validation. Some goAML portal implementations accept 1/0 but this varies; always use true/false for maximum compatibility.
Enumeration Constraints: Transaction and ID Types
The goAML schema defines strict enumeration sets for elements including transaction_type, id_type, account_type, gender, address_type, and submission_code. Every value inserted into these elements must exactly match one of the allowed values defined in the XSD. If your core banking system uses different code tables — for example, storing transaction types as numeric codes like 01, 02, 03 — a code mapping layer is required to translate these to the goAML enumeration values before XML generation.
This mapping is also country-specific. Kenya FRC's extended schema adds additional transaction type values for mobile money channels (MOBILE_WALLET_MPESA, MOBILE_WALLET_AIRTEL, MOBILE_WALLET_TKASH) that are not in the base UNODC schema. Generating a report with a base-schema transaction type for a mobile money transaction submitted to Kenya FRC will pass base XSD validation but fail country-level validation.
Whitespace and Empty Elements
The goAML schema has specific rules about empty elements versus missing elements. For optional elements that have no value, the correct approach is to omit the element entirely from the generated XML — not to include an empty tag. <fiu_ref_number></fiu_ref_number> is technically well-formed XML, but some goAML portal implementations reject it as schema-invalid when the element is defined as having a minLength constraint of 1. The correct behaviour is to omit fiu_ref_number entirely when there is no value to populate.
Conversely, elements that are mandatory and have a value must not have leading or trailing whitespace. <institution_code> KE-FRC-12345 </institution_code> will fail validation in strict implementations. All string values should be trimmed before insertion.
Country-Specific XML Extensions
How Kenya FRC Extends the Base XSD
Kenya's Financial Reporting Centre publishes a validation profile that extends the base UNODC XSD with additional mandatory requirements. The most significant Kenya-specific extensions are:
National ID mandate: For any Kenyan national appearing as a from_person or to_person in a report, the id_number and id_type elements are mandatory — not optional as they are in the base schema. The id_type must be NATIONAL_ID for Kenyan citizens. Foreign nationals must provide PASSPORT.
Mobile money channel classification: Transactions involving M-PESA, Airtel Money, or T-Kash must use the FRC's extended transaction_type enumeration values (MOBILE_WALLET_MPESA, MOBILE_WALLET_AIRTEL, MOBILE_WALLET_TKASH) and must include the mobile_money_agent_code element when the transaction was conducted through an agent.
Birthdate requirement for reporting person: While the base UNODC schema treats birthdate as optional for the reporting_person element, Kenya FRC validation rules make it mandatory. Reports without a reporting person birthdate are rejected.
CTR threshold: Kenya's CTR threshold is USD 15,000 or its equivalent in any other currency, under POCAMLA s.44(6) and Regulation 40(1) of POCAMLR 2023 (FRC Circular No. 4 of 2023). Every individual cash transaction at or above the threshold must be reported, with the original currency amount, applied exchange rate, and USD-equivalent captured in the CTR XML. Sub-threshold same-day activity is not aggregated into a CTR; where it appears to be structuring, the institution files an STR.
Zambia FIC Extensions
The Zambia Financial Intelligence Centre applies its own validation profile on top of the base UNODC schema. Key differences from Kenya include the currency code (ZMW), different CTR thresholds, and Zambia-specific transaction type enumerations. The FIC also requires the Business Registration Number for entity parties, which maps to a different field than the Kenya FRC's preference.
Institutions expanding from Kenya to Zambia cannot reuse a Kenya-configured XML generation engine without modification to the validation profile configuration.
Why a Single XML Template Does Not Work Across All Countries
This is the core reason that off-the-shelf goAML XML generators built for a single country fail when deployed in multi-country environments. The base UNODC XSD provides the structural envelope. Country FIUs then apply validation rules that modify which elements are mandatory, extend the enumeration sets, and add country-specific elements entirely. A template hard-coded for Kenya FRC will generate invalid XML for Uganda FIA and vice versa.
A production multi-country XML generation engine must be driven by country-specific configuration profiles that declare the applicable validation rules, mandatory fields, enumeration extensions, and FIU endpoint details for each target country.
XSD Validation in Code
.NET XML Schema Validation Using XmlSchemaSet
In .NET, goAML XSD validation is performed using the System.Xml.Schema.XmlSchemaSet class combined with an XmlReader configured with validation settings. The process loads the XSD file (or files, if the country extension is a separate overlay XSD), adds it to the schema set, and validates the generated XML document against it before attempting submission.
Validation errors are surfaced through a ValidationEventHandler callback, which should capture the full error message, the line number in the source XML, and the schema element path. These three pieces of information are essential for diagnosing which part of the report structure failed validation and why. Production implementations should log all validation errors with the full report context to enable rapid diagnosis and correction.
The validation step must occur after XML generation is complete but before the document is serialised to the submission payload. Attempting to validate mid-generation (while the document is still being built) produces misleading errors because mandatory elements that will be added later are flagged as missing.
Python lxml Validation
Python developers using the lxml library have access to the lxml.etree.XMLSchema class for XSD-based validation. The pattern is to parse the XSD file into an XMLSchema object, then call .validate() against the generated document tree. The XMLSchema.error_log attribute provides the full list of validation errors with path information.
One subtlety in lxml: the library is strict about namespace handling. The goAML schema's xmlns declaration must appear on the root element and must match exactly the namespace declared in the XSD. A common mistake is to generate an XML file with a slightly different namespace URI (for example, with a trailing slash, or with en vs EN) that causes the schema to fail with a "no matching global element declaration" error rather than a specific field-level error.
Java JAXB Binding Approach
Java developers working with goAML typically use JAXB (Java Architecture for XML Binding) to generate Java class bindings directly from the XSD. The xjc compiler reads the XSD and produces annotated Java classes for each schema type, along with marshalling and unmarshalling code. Report generation then becomes a matter of constructing Java object graphs and marshalling them to XML — the JAXB marshaller applies schema constraints automatically during serialisation.
The JAXB approach has the advantage of compile-time type safety: fields with the wrong type or missing mandatory values become compiler errors rather than runtime validation failures. The disadvantage is that regenerating the JAXB bindings when the XSD changes requires recompilation and redeployment of dependent code.
Online XSD Validation Tools for Testing
During development and schema update testing, online XSD validation tools provide a rapid feedback loop without requiring a local development environment. Tools such as FreeFormatter.com and XMLValidation.com accept XSD and XML file uploads and return detailed validation error messages. These are useful for quick sanity checks but should not replace automated validation in the deployment pipeline — online tools may not support all XSD schema features, and sensitive customer data should never be uploaded to public validators.
Building a Robust XML Generation Pipeline
A production-grade goAML XML generation pipeline has five distinct layers, each with a specific responsibility.
1. Data Normalisation Layer
Incoming transaction data from core banking systems arrives in formats that are not immediately usable in goAML XML. The normalisation layer handles date format conversion (DD/MM/YYYY → YYYY-MM-DD), amount precision normalisation, character encoding standardisation, and stripping of special characters that are illegal in XML (null bytes, certain control characters). It also applies field length truncation where incoming data exceeds the maximum length defined in the schema.
2. Business Rule Validation Layer
Before XML generation begins, the business rule validation layer checks that the assembled case data satisfies all semantic requirements for the report type. For a CTR: is the transaction amount at or above threshold? Is the account number present? Is the transaction type a cash transaction type? For an STR: is there at least one indicator code? Is the narrative non-empty and of adequate length? Is is_suspicious set correctly?
Catching semantic errors at this layer, before XML generation, produces far more useful error messages than catching them as XSD validation failures after generation.
3. XML Generation Layer
The XML generation layer takes the normalised, business-rule-validated data and constructs the goAML XML document. Schema-aware builders — classes or functions that construct specific schema elements from data model objects — are the recommended architectural pattern. Each builder is responsible for one element type (PersonBuilder, AccountBuilder, TransactionBuilder) and applies the appropriate formatting, enumeration mapping, and element inclusion/exclusion logic for the target country's profile.
Generated XML should be serialised to a string or byte array in memory before being written to disk or submitted, to allow the validation layer to inspect it before it leaves the system.
4. Post-Generation Validation Layer
The post-generation validation layer applies the loaded country-specific XSD to the generated XML document. All validation errors are logged with full context. If any validation errors are present, the report is flagged for review rather than submitted. The compliance team sees the specific field that failed validation and the schema rule that it violated, enabling rapid diagnosis without requiring developer involvement.
5. Submission and Tracking Layer
Once the XML document passes post-generation validation, the submission layer transmits it to the FIU portal. Submission responses — acceptance confirmations, rejection messages with reason codes, and FIU-assigned reference numbers — are captured and stored against the original report record. Pending responses are polled until a final status is received. Rejection details are surfaced to the compliance team with guidance for correction and resubmission.
When to Use an Off-the-Shelf XML Generation Engine
Maintenance Burden When XSD Is Updated by UNODC
UNODC has released multiple XSD versions for the goAML schema, and this pattern will continue. When a new XSD version is deployed by a national FIU, in-house XML generation code requires updating, testing, and deployment — often under time pressure, because FIUs set deadlines for migration. If your XML generation engine is maintained internally, every schema update lands on your development team's plate, competing with other development priorities.
Country-Specific Rule Updates
Kenya FRC publishes updated validation guidance periodically. When a new transaction type enumeration is added (for example, a new mobile money operator entering the market), every hardcoded enumeration mapping in your in-house codebase must be updated. When a new mandatory field is introduced for STR reports, your case management data model must be extended, your workflow must be updated to capture the new data, and your XML generation code must be updated to populate the new element.
An off-the-shelf platform carries these updates in its licensing model — schema changes are the vendor's problem, not yours.
Build vs. Buy Decision Framework
| Consideration | Build In-House | Off-the-Shelf Platform |
|---|---|---|
| Initial capability timeline | 18–24 months | 6–8 weeks |
| XSD update responsibility | Internal dev team | Vendor |
| Country expansion cost | Full re-implementation | Configuration only |
| Schema expertise required | Yes — ongoing | No |
| Customisation flexibility | High | Moderate–High |
| Total 3-year cost | $440K–$760K | $90K–$240K |
For institutions with large in-house development teams, deep compliance technology expertise, and a strategic need for highly customised XML generation logic, in-house development may be justified. For the majority of East African financial institutions, the economic and risk profile strongly favours a purpose-built platform maintained by specialists in goAML schema compliance.
Take the Next Step
Creodata's goAML AML Reporting Platform includes a production-ready XML generation engine maintained against the current goAML XSD v5.0.2, with country-specific validation profiles for Kenya FRC, Uganda FIA, Tanzania FIST, Zambia FIC, and Rwanda FIU. The engine handles encoding, decimal formatting, date normalisation, enumeration mapping, and XSD validation automatically — compliance teams interact with structured forms, not XML.
See the XML generation engine in action with a live demonstration: Request a Demo at creodata.com/demo
