Category: XML Templates Last Updated: 2026-02-12 Applies To: EU DAC8 / OECD CARF XML Schema
---
Overview
One of the most practical challenges in DAC8 compliance is mapping your existing platform database fields to the XML elements required by the OECD CARF schema. This article provides a concrete mapping reference that crypto-asset service providers can use as a starting point when building their XML generation pipeline.
The mappings below assume a typical crypto exchange or wallet platform database schema. Your actual table and column names will differ, but the concepts should translate directly.
User Identity Mapping
Individual Users
| DAC8 XML Element | Typical DB Field | Notes |
|---|---|---|
Individual/Name/FirstName | users.first_name | As provided during KYC/self-certification |
Individual/Name/MiddleName | users.middle_name | Optional; omit XML element if NULL |
Individual/Name/LastName | users.last_name | As provided during KYC/self-certification |
Individual/Address/CountryCode | users.country_code or addresses.country | ISO 3166-1 alpha-2 |
Individual/Address/Street | addresses.street_line1 | May need to concatenate line1 + line2 |
Individual/Address/City | addresses.city | |
Individual/Address/PostCode | addresses.postal_code | |
Individual/TIN | user_tax_info.tin | Must include issuedBy attribute |
Individual/TIN@issuedBy | user_tax_info.tin_country | ISO country code of TIN issuer |
Individual/BirthInfo/BirthDate | users.date_of_birth | Format: YYYY-MM-DD |
Individual/BirthInfo/City | users.birth_city | May not be collected; check requirements |
Individual/BirthInfo/CountryCode | users.birth_country | ISO 3166-1 alpha-2 |
Entity Users
| DAC8 XML Element | Typical DB Field | Notes |
|---|---|---|
Entity/Name | companies.legal_name | Full registered legal name |
Entity/Address/CountryCode | companies.country_code | ISO 3166-1 alpha-2 |
Entity/TIN | companies.tax_id | Business tax identification number |
Account Mapping
| DAC8 XML Element | Typical DB Field | Notes |
|---|---|---|
DocRefId | Generated | Must be globally unique; typically composed from jurisdiction + year + sequence |
AccountNumber | users.user_id or accounts.account_id | Your platform's internal identifier |
Transaction Mapping
SQL Query Example
A typical approach to extract transaction data for DAC8 reporting involves aggregating by user, transaction type, and crypto-asset:
SELECT
t.user_id,
CASE
WHEN t.type = 'sell' AND t.settlement_currency_type = 'fiat'
THEN 'EXCHANGE_FIAT'
WHEN t.type = 'sell' AND t.settlement_currency_type = 'crypto'
THEN 'EXCHANGE_CRYPTO'
WHEN t.type = 'withdrawal' AND t.destination_type = 'external'
THEN 'TRANSFER'
WHEN t.type = 'payment'
THEN 'RETAIL_PAYMENT'
END AS dac8_transaction_type,
t.crypto_asset_code,
COUNT(*) AS num_transactions,
SUM(t.gross_value_eur) AS aggregate_value_eur,
SUM(t.crypto_amount) AS total_units
FROM transactions t
WHERE t.executed_at >= '2026-01-01'
AND t.executed_at < '2027-01-01'
AND t.status = 'completed'
GROUP BY t.user_id, dac8_transaction_type, t.crypto_asset_code;
Transaction Field Mapping
| DAC8 XML Element | Typical DB Field | Notes |
|---|---|---|
TransactionType | Derived from transactions.type + context | See mapping logic above |
NumberOfTransactions | COUNT(*) | Count of completed transactions in category |
AggregateGrossProceeds | SUM(transactions.gross_value_eur) | Total in reporting currency |
AggregateGrossProceeds@currCode | Constant (e.g., EUR) | ISO 4217 currency code |
CryptoAssetDetails/AssetCode | transactions.crypto_asset_code | e.g., BTC, ETH, USDT |
CryptoAssetDetails/TotalUnits | SUM(transactions.crypto_amount) | Total units of the crypto-asset |
MessageSpec Mapping
| DAC8 XML Element | Source | Notes |
|---|---|---|
SendingCompanyIN | Configuration / company_settings.tax_id | Static per reporting entity |
TransmittingCountry | Configuration | Country of RCASP registration |
ReceivingCountry | user_tax_info.tax_residence_country | Determines which file the user goes into |
MessageType | Constant: CARF | |
MessageRefId | Generated | Unique per message; store for corrections |
ReportingPeriod | Constant: 2026-12-31 | Last day of reporting calendar year (first DAC8 period) |
Timestamp | NOW() at generation time | ISO 8601 UTC |
Handling Common Data Gaps
Missing TIN
If a user has not provided a TIN, check whether the schema allows a reason code (such as "TIN not issued by jurisdiction" or "TIN not yet obtained"). Do not invent a TIN value. Document why the TIN is missing.
<TIN issuedBy="FR" TINType="TIN_NOT_AVAILABLE">NOTIN</TIN>
> Caution: The exact mechanism for indicating a missing TIN may vary by schema version and jurisdiction. Verify the approach with your tax authority.
Missing Birth City / Country
If your platform did not historically collect birth city or birth country, and these fields are required by the schema, you may need to:
- Request this information from affected users through a self-certification update
- Use the schema's mechanism for indicating unavailable data, if one exists
Address Format Differences
If your database stores addresses as a single free-text field:
<Address>
<stf:CountryCode>FR</stf:CountryCode>
<stf:AddressFree>15 Rue de Rivoli, 75001 Paris, France</stf:AddressFree>
</Address>
If your database stores structured address components, map them to the structured XML elements for better data quality.
Building the XML Generation Pipeline
A recommended approach:
- Extract reportable user and transaction data using SQL queries similar to the example above
- Transform the data by applying mappings, currency conversions, and aggregation logic
- Group records by receiving jurisdiction (one XML file per jurisdiction)
- Generate XML using a templating engine or XML library that respects the XSD schema
- Validate each generated file against the official XSD before submission
- Store all generated MessageRefIds and DocRefIds for future correction reference
Currency Conversion Considerations
If your platform stores transaction values in multiple currencies, you must convert all values to the reporting currency (typically EUR for EU jurisdictions). Use exchange rates from a reputable source as of the transaction date. Document your exchange rate source and methodology, as tax authorities may request this information.
-- Example: converting USD values to EUR using daily rates
SELECT
t.crypto_amount,
t.gross_value_usd * fx.rate_to_eur AS gross_value_eur
FROM transactions t
JOIN fx_rates fx ON fx.date = DATE(t.executed_at) AND fx.from_currency = 'USD';
Keep records of the exchange rates used so that you can justify your reported values if queried.
Need help with DAC8 reporting?
Our team handles XML generation, TIN validation, and submission for CASPs across all 27 EU Member States.