Skip to main content

Italy Fiscalization Guide

What is Italian fiscalization?

Italy requires businesses to electronically report every invoice and receipt to the government. The specific system depends on who the customer is:

  • Selling to another business (B2B) or to the government (B2G)? → Use SDI / FatturaPA (electronic invoicing)
  • Selling to a consumer at a retail POS (B2C)? → Use Documento Commerciale (electronic receipts)

Think of it this way: if your merchant issues formal invoices (consulting firms, wholesalers, government contractors), they use SDI. If they run a cash register (cafés, retail shops, restaurants), they use Documento Commerciale. Some merchants need both -- a restaurant chain might use Documento Commerciale for daily counter sales and SDI for catering invoices.

Zyntem routes transactions to the correct system based on the location's country_config. You just create transactions -- the adapter handles the rest.


SDI / FatturaPA (B2B and B2G invoicing)

What is SDI?

SDI (Sistema di Interscambio -- "Exchange System") is Italy's central clearinghouse for electronic invoices. Every B2B and B2G invoice in Italy must pass through SDI. Think of it as a government-operated mail system for invoices: your merchant sends the invoice to SDI, and SDI delivers it to the recipient.

FatturaPA is the standardized XML format that invoices must use. The name literally means "Electronic Invoice for Public Administration," though it's now required for all B2B invoices too.

What problem does it solve?

Before SDI, businesses could send invoices in any format (paper, PDF, email). The tax authority had no visibility into transactions until the annual tax return. SDI gives them real-time visibility into every B2B transaction in the country.

How it works

Italy's SDI flow is asynchronous -- you submit the invoice, and SDI processes it in the background:

  1. Your app creates a transaction via POST /v1/transactions
  2. Zyntem generates the FatturaPA XML and submits it to SDI
  3. SDI returns a tracking number (called IdentificativoSdI) -- your transaction enters pending status
  4. SDI validates the invoice and attempts to deliver it to the recipient (this can take minutes to days)
  5. SDI sends a notification back to Zyntem with the result
  6. Your transaction status updates to delivered, rejected, or delivery_error

Example: When your merchant's ERP system submits a EUR 10,000 consulting invoice to a client in Milan, Zyntem generates the FatturaPA XML, sends it to SDI, and immediately returns a pending transaction with the tracking number. A few minutes later, SDI confirms delivery, and Zyntem updates the transaction to delivered. You receive a webhook notification with the status change.

You don't need to worry about this

Zyntem handles FatturaPA XML generation, SDI submission, and async notification processing. You don't need to understand the XML schema or poll for status updates -- just subscribe to webhooks.

FatturaPA document types

SDI uses document type codes (TD codes) to classify what kind of invoice you're submitting. Zyntem maps your transaction type automatically:

Your transaction typeWhat Zyntem sendsWhen to use
saleTD01 -- Standard invoice (Fattura)Normal B2B sale
refundTD04 -- Credit note (Nota di credito)Issuing a refund or correction
voidTD04 -- Credit note (Nota di credito)Cancelling a previous invoice

Zyntem also supports these additional document types via metadata:

CodeWhat it isWhen to use
TD02Advance payment invoiceCustomer pays a deposit before delivery
TD06Fee note (Parcella)Professional services invoices (lawyers, accountants)
TD07Simplified invoiceSmall invoices under EUR 400 (fewer required fields)
TD24Deferred invoiceGoods delivered with a transport document, invoiced later
Full TD code reference

Italy defines 20+ document type codes for specialized scenarios (intra-EU supplies, reverse charge, VAT warehouse operations, etc.). Zyntem supports all of them. For the full list, see the FatturaPA specification.

SDI submission flow

POST /v1/transactions


┌─────────────────┐
│ Generate XML │ FatturaPA v1.2.2
│ (FatturaPA) │
└────────┬────────┘


┌─────────────────┐
│ Submit to SDI │ REST API with Bearer token
│ │ Returns IdentificativoSdI
└────────┬────────┘


Status: pending

┌────┴────────────────┐
│ SDI processes │ (async, minutes to days)
│ async │
└────┬────────────────┘

┌────┼────────┐
▼ ▼ ▼
RC NS MC
delivered rejected delivery_error

Async notification lifecycle

After submission, SDI sends notifications back to Zyntem. Each notification updates your transaction:

NotificationWhat it meansYour transaction statusWhat you should do
RC (RicevutaConsegna)Invoice delivered to the recipientdeliveredNothing -- the invoice is complete
NS (NotificaScarto)SDI rejected the invoice (validation errors)rejectedFix the data and create a new transaction
MC (NotificaMancataConsegna)SDI accepted it but couldn't deliver to recipientdelivery_errorThe invoice is still valid -- the recipient can retrieve it from their SDI portal

Subscribing to status updates

Use Zyntem webhooks to get notified when SDI updates your transaction:

curl -X POST https://api.zyntem.dev/v1/webhooks \
-H "Content-Type: application/json" \
-H "Authorization: Bearer zyn_test_abc123def456..." \
-d '{
"url": "https://example.com/webhooks/zyntem",
"events": ["fiscalization.completed", "fiscalization.dead_letter"],
"description": "Italy SDI notifications"
}'

SDI country config fields

FieldTypeRequiredDescription
codice_fiscalestringYesThe merchant's Italian tax code (11-16 alphanumeric characters)

Note: The Partita IVA (Italy's 11-digit VAT number, similar to a sales tax permit number) goes in the location's tax_id field, not in country_config.

| formato_trasmissione | string | Yes | FPA12 for invoices to government entities, FPR12 for B2B invoices | | regime_fiscale | string | Yes | Tax regime code (see table below) | | api_credential_id | string | No | Reference to stored SDI API credentials | | progressivo_invio | string | No | Transmission sequence prefix (auto-generated if empty) |

Tax regime codes

CodeWhat it meansWhen to use
RF01Standard regime (Regime ordinario)Most businesses -- use this if unsure
RF02Small taxpayer regime (Contribuenti minimi)Very small businesses with simplified accounting
RF04Agricultural regimeFarms and agricultural businesses
RF19Flat-rate regime (Regime forfettario)Small businesses/freelancers with simplified flat-rate taxation

SDI configuration example

B2B location

curl -X POST https://api.zyntem.dev/v1/locations \
-H "Content-Type: application/json" \
-H "Authorization: Bearer zyn_test_abc123def456..." \
-d '{
"name": "Milan Office",
"country": "IT",
"address": "Via Montenapoleone 1, 20121 Milano",
"tax_id": "12345678901",
"country_config": {
"codice_fiscale": "RSSMRA85M01H501Z",
"formato_trasmissione": "FPR12",
"regime_fiscale": "RF01"
}
}'

Public Administration location

curl -X POST https://api.zyntem.dev/v1/locations \
-H "Content-Type: application/json" \
-H "Authorization: Bearer zyn_test_abc123def456..." \
-d '{
"name": "Rome PA Office",
"country": "IT",
"address": "Via del Corso 1, 00186 Roma",
"tax_id": "12345678901",
"country_config": {
"codice_fiscale": "12345678901",
"formato_trasmissione": "FPA12",
"regime_fiscale": "RF01"
}
}'

Creating an SDI transaction

Include Italy-specific metadata when creating transactions:

curl -X POST https://api.zyntem.dev/v1/transactions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer zyn_test_abc123def456..." \
-d '{
"location_id": "loc_abc123",
"type": "sale",
"amount": 12200,
"currency": "EUR",
"metadata": {
"invoice_number": "FT-2026-001",
"invoice_date": "2026-03-13",
"codice_destinatario": "ABC1234",
"buyer_denomination": "Buyer Corp SRL",
"buyer_country": "IT"
},
"line_items": [
{
"description": "Consulting services",
"quantity": 1,
"unit_price": 10000,
"vat_rate": 2200
}
]
}'

Transaction metadata fields

FieldRequiredDefaultDescription
invoice_numberYes--Your invoice number (becomes the Numero in FatturaPA)
invoice_dateNoTransaction creation dateInvoice date in YYYY-MM-DD format
codice_destinatarioNo0000000The recipient's SDI channel code (7 characters) -- their "mailbox" in the SDI system
buyer_denominationNo--Buyer's company name
buyer_countryNoITBuyer's country code (ISO 3166-1 alpha-2)

SDI validation rules

RuleDetail
partita_iva requiredMust be exactly 11 digits (set in location's tax_id)
codice_fiscale requiredMust be 11-16 alphanumeric characters (uppercase)
formato_trasmissione requiredMust be FPA12 or FPR12
regime_fiscale requiredMust match pattern RF01 through RF19
invoice_number requiredMust be present in transaction metadata

SDI error examples

Missing Partita IVA:

{
"error": "partita_iva is required"
}

Invalid formato trasmissione:

{
"error": "formato_trasmissione must be FPA12 or FPR12"
}

Missing invoice number:

{
"error": "invoice_number is required in metadata for Italian transactions"
}

Sandbox testing (SDI)

Sandbox routing is automatic when using a test API key (zyn_test_...). Test transactions hit sandbox endpoints without affecting production data.

curl -X POST https://api.zyntem.dev/v1/transactions \
-H "Authorization: Bearer zyn_test_abc123def456..." \
-d '{
"location_id": "loc_abc123",
"type": "sale",
"amount": 12200,
"currency": "EUR",
"metadata": {
"invoice_number": "TEST-001"
}
}'

Documento Commerciale (B2C retail)

What is Documento Commerciale?

The Documento Commerciale (DC) is Italy's electronic receipt system for retail POS transactions. It replaced the old paper receipt (the "scontrino fiscale") -- now every retail sale must be electronically transmitted to the Agenzia delle Entrate (AdE, Italy's tax authority).

What problem does it solve?

Italy's tax authority wants real-time visibility into retail sales, not just B2B invoices. Every coffee, every pair of shoes, every restaurant meal gets reported. This reduces unreported cash sales.

How it works

Zyntem uses the Software Solution (SSW) approach, introduced January 1, 2026. This is a fully software-based method that eliminates the need for special hardware (called a "Registratore Telematico" or RT) at each register.

The DC flow is asynchronous with polling:

  1. Your POS creates a transaction via POST /v1/transactions with a DC-configured location
  2. Zyntem generates the receipt XML (DSW10 format) and seals it with an electronic seal
  3. The sealed document is submitted to the AdE SSW API
  4. AdE returns a tracking ID -- your transaction enters pending status
  5. Zyntem polls AdE for the result
  6. Your transaction status updates to success or failed

Example: When a customer buys two cappuccinos (EUR 1.50 each) and a croissant (EUR 1.20) at a café in Rome, paying EUR 4.20 by card, your POS sends the sale to Zyntem. The adapter generates the electronic receipt, seals it, and submits it to AdE. The transaction completes within seconds.

You don't need to worry about this

Zyntem handles XML generation, electronic sealing, submission, and polling for results. You don't need to manage eIDAS certificates or understand the DSW10 format. Just create a transaction with the sale details.

B2B vs B2C: which system does my location use?

Zyntem routes automatically based on how you configure the location:

Location configSystem usedTypical use case
formato_trasmissione: "FPR12" or "FPA12"SDI / FatturaPAB2B and B2G invoicing
system: "dc"Documento Commerciale / SSWB2C retail receipts

A single merchant may have both types of locations -- for example, a retailer with SDI locations for wholesale invoicing and DC locations for store registers.

DC country config fields

FieldTypeRequiredDescription
systemstringYesMust be "dc" for Documento Commerciale
codice_fiscalestringYesThe merchant's Italian tax code (11-16 alphanumeric characters)

Note: The Partita IVA (11-digit VAT number) goes in the location's tax_id field, not in country_config.

| legal_name | string | Yes | Business legal name | | cau_code | string | Yes | Provider code (4 alphanumeric characters, assigned by AdE) | | pem_matricola | string | Yes | Emission point ID -- identifies this specific register (format: XXXX-XXXXXX, 11 characters) | | seal_certificate_id | string | Yes | Reference to the electronic seal certificate (issued by a qualified trust service provider) |

DC configuration example

curl -X POST https://api.zyntem.dev/v1/locations \
-H "Content-Type: application/json" \
-H "Authorization: Bearer zyn_test_abc123def456..." \
-d '{
"name": "Rome Retail Store",
"country": "IT",
"address": "Via del Corso 100, 00186 Roma",
"tax_id": "01234567890",
"country_config": {
"system": "dc",
"partita_iva": "01234567890",
"codice_fiscale": "01234567890",
"legal_name": "Negozio Roma SRL",
"cau_code": "AB12",
"pem_matricola": "AB12-000001",
"seal_certificate_id": "cert_seal_abc123"
}
}'

DC transaction metadata

DC transactions use different metadata fields than SDI invoices:

Payment fields

FieldRequiredDescription
cash_amountConditionalCash payment amount (required if the customer pays with cash)
electronic_amountConditionalCard/transfer payment amount (required if the customer pays electronically)
ticket_amountNoVoucher payment amount
ticket_countNoNumber of vouchers used

Optional customer fields

FieldRequiredDescription
lottery_codeNoCustomer's receipt lottery code (Italy runs a lottery tied to electronic receipts to incentivize consumers to request receipts)
instant_lottery_qrNoInstant lottery QR code
customer_codice_fiscaleNoCustomer's tax code (only if the customer explicitly requests it)

Return and cancellation fields

FieldRequiredDescription
is_returnYes (for returns)Set to true for a product return
is_cancellationYes (for cancellations)Set to true to cancel a previous receipt
original_matricolaYes (for returns/cancellations)The register ID that issued the original receipt
original_dateYes (for returns/cancellations)Date of the original receipt (ISO format)
original_numberYes (for returns/cancellations)Sequential number of the original receipt (format: NNNN-NNNN)

Creating a DC transaction

Example: A customer buys two cappuccinos and two croissants at a café in Rome, paying EUR 5.80 by card.

curl -X POST https://api.zyntem.dev/v1/transactions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer zyn_test_abc123def456..." \
-d '{
"location_id": "loc_dc_roma",
"timestamp": "2026-03-15T14:30:00Z",
"items": [
{
"description": "Cappuccino",
"quantity": 2,
"unit_price": 1.50,
"tax_rate": 10.00,
"tax_amount": 0.30,
"total_amount": 3.30
},
{
"description": "Cornetto",
"quantity": 2,
"unit_price": 1.20,
"tax_rate": 4.00,
"tax_amount": 0.10,
"total_amount": 2.50
}
],
"pretax_amount": 5.40,
"tax_amount": 0.40,
"total_amount": 5.80,
"currency": "EUR",
"payment_method": "card",
"metadata": {
"electronic_amount": 5.80
}
}'

Creating a DC return

Example: The customer returns one croissant (EUR 1.25 including tax).

curl -X POST https://api.zyntem.dev/v1/transactions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer zyn_test_abc123def456..." \
-d '{
"location_id": "loc_dc_roma",
"timestamp": "2026-03-15T15:00:00Z",
"items": [
{
"description": "Cornetto (reso)",
"quantity": 1,
"unit_price": 1.20,
"tax_rate": 4.00,
"tax_amount": 0.05,
"total_amount": 1.25
}
],
"pretax_amount": 1.20,
"tax_amount": 0.05,
"total_amount": 1.25,
"currency": "EUR",
"payment_method": "card",
"metadata": {
"is_return": true,
"electronic_amount": 1.25,
"original_matricola": "AB12-000001",
"original_date": "2026-03-15",
"original_number": "0001-0042"
}
}'

Italian VAT rates

Italy uses multiple VAT rates. Here's what you'll encounter for retail transactions:

RateWhat it applies toExamples
22%Standard rateMost goods and services (electronics, clothing, professional services)
10%Reduced rateRestaurant meals, hotel stays, some prepared food, renovations
5%Lower reduced rateCertain food items, social services
4%Super-reduced rateBasic necessities (bread, milk, fresh produce)

For items that are exempt from VAT, use Natura codes instead of a rate:

CodeWhat it meansWhen to use
N1Excluded from VATItems excluded by law (e.g., stamp duty)
N2Not subject to VATOperations outside the scope of VAT
N3Non-taxableExports and similar non-taxable operations
N4ExemptVAT-exempt operations (e.g., medical services)
N5Margin schemeUsed goods sold under the margin scheme
N6Other non-VAT operationsCatch-all for other non-VAT scenarios

SSW submission flow

POST /v1/transactions


┌──────────────────┐
│ Generate XML │ DSW10 format
│ (Doc. Commerciale)│
└────────┬─────────┘


┌──────────────────┐
│ Seal with eIDAS │ QTSP advanced electronic seal
│ certificate │
└────────┬─────────┘


┌──────────────────┐
│ POST /corrispettivi│ Binary octet-stream
│ to AdE SSW API │ Returns idPresaInCarico
└────────┬─────────┘


Status: pending

┌────┴──────────────────────┐
│ Poll /corrispettivi/ │
│ {id}/stato │
└────┬──────────────────────┘

┌────┼──────────┐
▼ ▼ ▼
PRONTA IN_ELAB. NON_DISP.


GET /esito

┌─┴──┐
▼ ▼
success failed

SSW error codes

If AdE rejects a submission:

CodeWhat went wrongCan you retry?
0100Non-compliant file (XML format error)No -- fix the data and resubmit
0200Validation error (missing/invalid fields)No -- fix the data and resubmit
0403Not authorized (certificate issue)No -- check your seal certificate
0429Rate limit exceeded (too many requests)Yes -- Zyntem retries automatically
0500AdE internal server errorYes -- Zyntem retries automatically
You don't need to worry about this

Zyntem translates Italian error descriptions to English in the transaction's error_message field and automatically retries retriable errors.

Sandbox testing (DC)

Use a test-mode API key (zyn_test_...) to route DC transactions to the AdE sandbox:

curl -X POST https://api.zyntem.dev/v1/transactions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer zyn_test_abc123def456..." \
-d '{
"location_id": "loc_dc_roma",
"timestamp": "2026-03-15T14:30:00Z",
"items": [
{
"description": "Test item",
"quantity": 1,
"unit_price": 10.00,
"tax_rate": 22.00,
"tax_amount": 2.20,
"total_amount": 12.20
}
],
"pretax_amount": 10.00,
"tax_amount": 2.20,
"total_amount": 12.20,
"currency": "EUR",
"payment_method": "cash",
"metadata": {
"cash_amount": 12.20
}
}'