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 ElementTypical DB FieldNotes
Individual/Name/FirstNameusers.first_nameAs provided during KYC/self-certification
Individual/Name/MiddleNameusers.middle_nameOptional; omit XML element if NULL
Individual/Name/LastNameusers.last_nameAs provided during KYC/self-certification
Individual/Address/CountryCodeusers.country_code or addresses.countryISO 3166-1 alpha-2
Individual/Address/Streetaddresses.street_line1May need to concatenate line1 + line2
Individual/Address/Cityaddresses.city
Individual/Address/PostCodeaddresses.postal_code
Individual/TINuser_tax_info.tinMust include issuedBy attribute
Individual/TIN@issuedByuser_tax_info.tin_countryISO country code of TIN issuer
Individual/BirthInfo/BirthDateusers.date_of_birthFormat: YYYY-MM-DD
Individual/BirthInfo/Cityusers.birth_cityMay not be collected; check requirements
Individual/BirthInfo/CountryCodeusers.birth_countryISO 3166-1 alpha-2

Entity Users

DAC8 XML ElementTypical DB FieldNotes
Entity/Namecompanies.legal_nameFull registered legal name
Entity/Address/CountryCodecompanies.country_codeISO 3166-1 alpha-2
Entity/TINcompanies.tax_idBusiness tax identification number

Account Mapping

DAC8 XML ElementTypical DB FieldNotes
DocRefIdGeneratedMust be globally unique; typically composed from jurisdiction + year + sequence
AccountNumberusers.user_id or accounts.account_idYour 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 ElementTypical DB FieldNotes
TransactionTypeDerived from transactions.type + contextSee mapping logic above
NumberOfTransactionsCOUNT(*)Count of completed transactions in category
AggregateGrossProceedsSUM(transactions.gross_value_eur)Total in reporting currency
AggregateGrossProceeds@currCodeConstant (e.g., EUR)ISO 4217 currency code
CryptoAssetDetails/AssetCodetransactions.crypto_asset_codee.g., BTC, ETH, USDT
CryptoAssetDetails/TotalUnitsSUM(transactions.crypto_amount)Total units of the crypto-asset

MessageSpec Mapping

DAC8 XML ElementSourceNotes
SendingCompanyINConfiguration / company_settings.tax_idStatic per reporting entity
TransmittingCountryConfigurationCountry of RCASP registration
ReceivingCountryuser_tax_info.tax_residence_countryDetermines which file the user goes into
MessageTypeConstant: CARF
MessageRefIdGeneratedUnique per message; store for corrections
ReportingPeriodConstant: 2026-12-31Last day of reporting calendar year (first DAC8 period)
TimestampNOW() at generation timeISO 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:

  1. Request this information from affected users through a self-certification update
  2. 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:

  1. Extract reportable user and transaction data using SQL queries similar to the example above
  2. Transform the data by applying mappings, currency conversions, and aggregation logic
  3. Group records by receiving jurisdiction (one XML file per jurisdiction)
  4. Generate XML using a templating engine or XML library that respects the XSD schema
  5. Validate each generated file against the official XSD before submission
  6. 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.

Get Expert Help