Build a CRS Report XML (OECD Schema)
Skill: Convert reportable-account data into the OECD Common Reporting Standard XML
Region: Global (OECD Common Reporting Standard — automatic exchange of financial account information)
Category: Tax — OECD CRS XML (Automatic Exchange of Information, AEOI)
Does: Takes a financial institution's reportable-account data and produces the OECD CRS XML for submission to a tax authority and onward exchange — the reporting-FI block, account-holder and controlling-person records, account balances/payments, TIN handling, nil reporting, and corrections (DocRefId/CorrDocRefId).
Schema version: OECD CRS XML Schema v3.0 (with MessageSpec, ReportingFI, ReportingGroup)
The AI assembles the XML from your data; it does not classify accounts or determine reportability. Per-jurisdiction TIN rules, residence determination, and onboarding due diligence are the FI's responsibility. Confirm your jurisdiction's CRS schema version and validation portal (many tax authorities add a local wrapper) before submission.
When this applies
- A Reporting Financial Institution (bank, custodian, investment entity, specified insurer) filing its annual CRS return.
- Nil reporting where a jurisdiction requires it even with no reportable accounts.
- Corrections / voids of a previously filed message using the
DocRefIdmechanism.
Build procedure
- Read the source — account register: account number, balance, currency, account-holder identity (name, address, residence country, TIN, date of birth), account type, and payments by type (interest/dividends/gross proceeds/other).
- Build the message header (
MessageSpec) — sending company IN, transmitting & receiving country, reporting period (year-end date), message type (CRS701), and a uniqueMessageRefId. - Build the
ReportingFI— the FI's name, address, residence country, and IN; setDocTypeIndic(OECD1new data,OECD0resend,OECD2/3correction/void). - Build each
AccountReport— account number,DocRefId, account holder (Individual or Organisation) withResCountryCode+TIN, controlling persons (for passive NFEs), account balance, andPaymentblocks. - Normalize — dates
YYYY-MM-DD;ResCountryCode/currency as ISO codes; TIN withissuedBy; omit-reason codes where a TIN is unavailable. - Assign DocRefIds — globally unique per record (
<sending-country><year>-<uuid>); corrections reference the priorDocRefIdinCorrDocRefId. - Emit the CRS XML, per the worked example.
- Validate — work through the checklist.
Source → CRS field map
| From the source | → CRS element |
|---|---|
| Sending FI IN | MessageSpec/SendingCompanyIN |
| Transmitting / receiving country | MessageSpec/TransmittingCountry, ReceivingCountry |
| Reporting year-end | MessageSpec/ReportingPeriod |
| Unique message reference | MessageSpec/MessageRefId |
| FI name / address / IN | ReportingFI/Name, Address, IN |
| Account number | AccountReport/AccountNumber |
| Record reference | AccountReport/DocSpec/DocRefId |
| Account-holder name | AccountHolder/Individual/Name |
| Residence country | .../ResCountryCode |
| Taxpayer ID | .../TIN (issuedBy) |
| Date of birth | Individual/BirthInfo/BirthDate |
| Account balance | AccountBalance (currCode) |
| Payments | Payment/Type + Payment/PaymentAmnt |
Document structure (CRS)
CRS_OECD (version 3.0)
├── MessageSpec (SendingCompanyIN, Transmitting/ReceivingCountry, MessageType,
│ MessageRefId, ReportingPeriod, MessageTypeIndic)
└── CrsBody
├── ReportingFI (ResCountryCode, IN, Name, Address, DocSpec)
└── ReportingGroup
└── AccountReport ...
├── DocSpec (DocTypeIndic, DocRefId[, CorrDocRefId])
├── AccountNumber
├── AccountHolder (Individual | Organisation + CtrlgPerson)
├── AccountBalance
└── Payment ...
Code tables
Document type indicator (DocTypeIndic)
| Code | Meaning |
|---|---|
OECD1 |
New data |
OECD0 |
Resent data |
OECD2 |
Corrected data |
OECD3 |
Deletion of data |
OECD10–OECD13 |
Test-data equivalents |
Message type indicator (MessageTypeIndic)
| Code | Meaning |
|---|---|
CRS701 |
New information |
CRS702 |
Corrections / deletions |
CRS703 |
No data to report (nil) |
Payment type
| Code | Meaning |
|---|---|
CRS501 |
Dividends |
CRS502 |
Interest |
CRS503 |
Gross proceeds / redemptions |
CRS504 |
Other (e.g. other CRS income) |
TIN unavailable (TIN issuedBy + unknown) / omit reasons
Use the account-holder's residence-jurisdiction TIN. Where none exists, follow the schema's TIN unknown="true" handling and any local explanation field.
Calculation rules
AccountBalanceis the year-end (or closure-date) value in the account currency; a closed account is reported with balance0and the closure flag.- Each
Payment/PaymentAmntaggregates the year's payments of that type; recompute from the transaction feed. DocRefIdmust be globally unique and never reused;CorrDocRefId(in aCRS702message) must exactly match the prior record'sDocRefId.- A
CRS703nil message contains aReportingFIbut noAccountReport.
Worked example (end-to-end)
Input — one reportable individual account
Holder: Maria Schmidt, resident DE, TIN DE0123456789, DOB 1980-05-12; account CH93-0000-0000-0000-1 balance €85,000.00; interest paid €1,250.00.
Output — CRS XML
<?xml version="1.0" encoding="UTF-8"?>
<CRS_OECD xmlns="urn:oecd:ties:crs:v3" version="3.0">
<MessageSpec>
<SendingCompanyIN>CHE123456789</SendingCompanyIN>
<TransmittingCountry>CH</TransmittingCountry>
<ReceivingCountry>DE</ReceivingCountry>
<MessageType>CRS</MessageType>
<MessageRefId>CH2025-2f1c9b40</MessageRefId>
<MessageTypeIndic>CRS701</MessageTypeIndic>
<ReportingPeriod>2025-12-31</ReportingPeriod>
</MessageSpec>
<CrsBody>
<ReportingFI>
<ResCountryCode>CH</ResCountryCode>
<IN issuedBy="CH">CHE123456789</IN>
<Name>Alpine Private Bank AG</Name>
<Address>
<CountryCode>CH</CountryCode>
<AddressFree>Bahnhofstrasse 1, 8001 Zurich</AddressFree>
</Address>
<DocSpec>
<DocTypeIndic>OECD1</DocTypeIndic>
<DocRefId>CH2025-FI-0001</DocRefId>
</DocSpec>
</ReportingFI>
<ReportingGroup>
<AccountReport>
<DocSpec>
<DocTypeIndic>OECD1</DocTypeIndic>
<DocRefId>CH2025-AR-0001</DocRefId>
</DocSpec>
<AccountNumber>CH9300000000000001</AccountNumber>
<AccountHolder>
<Individual>
<ResCountryCode>DE</ResCountryCode>
<TIN issuedBy="DE">DE0123456789</TIN>
<Name>
<FirstName>Maria</FirstName>
<LastName>Schmidt</LastName>
</Name>
<Address>
<CountryCode>DE</CountryCode>
<AddressFree>Hauptstrasse 5, 10115 Berlin</AddressFree>
</Address>
<BirthInfo>
<BirthDate>1980-05-12</BirthDate>
</BirthInfo>
</Individual>
</AccountHolder>
<AccountBalance currCode="EUR">85000.00</AccountBalance>
<Payment>
<Type>CRS502</Type>
<PaymentAmnt currCode="EUR">1250.00</PaymentAmnt>
</Payment>
</AccountReport>
</ReportingGroup>
</CrsBody>
</CRS_OECD>
Validation checklist
-
MessageRefIdunique;MessageTypeIndicmatches content (CRS701/702/703) - Reporting FI block present; nil messages carry no
AccountReport - Every account holder has
ResCountryCodeand a TIN (or a valid omit reason) - Controlling persons reported for passive NFE organisation holders
-
DocRefIdglobally unique; corrections reference the priorDocRefIdinCorrDocRefId - Balances and payment amounts recomputed; payment types use
CRS501–CRS504 - Validated against the OECD CRS v3.0 XSD and your jurisdiction's portal schema
Last updated: 2026-06-12 — confirm the CRS schema version (v2.0 vs v3.0), your jurisdiction's local wrapper/portal rules, and current TIN-omission handling before submission.