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:
- Your app creates a transaction via
POST /v1/transactions - Zyntem generates the FatturaPA XML and submits it to SDI
- SDI returns a tracking number (called
IdentificativoSdI) -- your transaction enterspendingstatus - SDI validates the invoice and attempts to deliver it to the recipient (this can take minutes to days)
- SDI sends a notification back to Zyntem with the result
- Your transaction status updates to
delivered,rejected, ordelivery_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.
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 type | What Zyntem sends | When to use |
|---|---|---|
sale | TD01 -- Standard invoice (Fattura) | Normal B2B sale |
refund | TD04 -- Credit note (Nota di credito) | Issuing a refund or correction |
void | TD04 -- Credit note (Nota di credito) | Cancelling a previous invoice |
Zyntem also supports these additional document types via metadata:
| Code | What it is | When to use |
|---|---|---|
| TD02 | Advance payment invoice | Customer pays a deposit before delivery |
| TD06 | Fee note (Parcella) | Professional services invoices (lawyers, accountants) |
| TD07 | Simplified invoice | Small invoices under EUR 400 (fewer required fields) |
| TD24 | Deferred invoice | Goods delivered with a transport document, invoiced later |
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:
| Notification | What it means | Your transaction status | What you should do |
|---|---|---|---|
| RC (RicevutaConsegna) | Invoice delivered to the recipient | delivered | Nothing -- the invoice is complete |
| NS (NotificaScarto) | SDI rejected the invoice (validation errors) | rejected | Fix the data and create a new transaction |
| MC (NotificaMancataConsegna) | SDI accepted it but couldn't deliver to recipient | delivery_error | The 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
| Field | Type | Required | Description |
|---|---|---|---|
codice_fiscale | string | Yes | The 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_idfield, not incountry_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
| Code | What it means | When to use |
|---|---|---|
| RF01 | Standard regime (Regime ordinario) | Most businesses -- use this if unsure |
| RF02 | Small taxpayer regime (Contribuenti minimi) | Very small businesses with simplified accounting |
| RF04 | Agricultural regime | Farms and agricultural businesses |
| RF19 | Flat-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
| Field | Required | Default | Description |
|---|---|---|---|
invoice_number | Yes | -- | Your invoice number (becomes the Numero in FatturaPA) |
invoice_date | No | Transaction creation date | Invoice date in YYYY-MM-DD format |
codice_destinatario | No | 0000000 | The recipient's SDI channel code (7 characters) -- their "mailbox" in the SDI system |
buyer_denomination | No | -- | Buyer's company name |
buyer_country | No | IT | Buyer's country code (ISO 3166-1 alpha-2) |
SDI validation rules
| Rule | Detail |
|---|---|
partita_iva required | Must be exactly 11 digits (set in location's tax_id) |
codice_fiscale required | Must be 11-16 alphanumeric characters (uppercase) |
formato_trasmissione required | Must be FPA12 or FPR12 |
regime_fiscale required | Must match pattern RF01 through RF19 |
invoice_number required | Must 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:
- Your POS creates a transaction via
POST /v1/transactionswith a DC-configured location - Zyntem generates the receipt XML (DSW10 format) and seals it with an electronic seal
- The sealed document is submitted to the AdE SSW API
- AdE returns a tracking ID -- your transaction enters
pendingstatus - Zyntem polls AdE for the result
- Your transaction status updates to
successorfailed
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.
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 config | System used | Typical use case |
|---|---|---|
formato_trasmissione: "FPR12" or "FPA12" | SDI / FatturaPA | B2B and B2G invoicing |
system: "dc" | Documento Commerciale / SSW | B2C 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
| Field | Type | Required | Description |
|---|---|---|---|
system | string | Yes | Must be "dc" for Documento Commerciale |
codice_fiscale | string | Yes | The merchant's Italian tax code (11-16 alphanumeric characters) |
Note: The Partita IVA (11-digit VAT number) goes in the location's
tax_idfield, not incountry_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
| Field | Required | Description |
|---|---|---|
cash_amount | Conditional | Cash payment amount (required if the customer pays with cash) |
electronic_amount | Conditional | Card/transfer payment amount (required if the customer pays electronically) |
ticket_amount | No | Voucher payment amount |
ticket_count | No | Number of vouchers used |
Optional customer fields
| Field | Required | Description |
|---|---|---|
lottery_code | No | Customer's receipt lottery code (Italy runs a lottery tied to electronic receipts to incentivize consumers to request receipts) |
instant_lottery_qr | No | Instant lottery QR code |
customer_codice_fiscale | No | Customer's tax code (only if the customer explicitly requests it) |
Return and cancellation fields
| Field | Required | Description |
|---|---|---|
is_return | Yes (for returns) | Set to true for a product return |
is_cancellation | Yes (for cancellations) | Set to true to cancel a previous receipt |
original_matricola | Yes (for returns/cancellations) | The register ID that issued the original receipt |
original_date | Yes (for returns/cancellations) | Date of the original receipt (ISO format) |
original_number | Yes (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:
| Rate | What it applies to | Examples |
|---|---|---|
| 22% | Standard rate | Most goods and services (electronics, clothing, professional services) |
| 10% | Reduced rate | Restaurant meals, hotel stays, some prepared food, renovations |
| 5% | Lower reduced rate | Certain food items, social services |
| 4% | Super-reduced rate | Basic necessities (bread, milk, fresh produce) |
For items that are exempt from VAT, use Natura codes instead of a rate:
| Code | What it means | When to use |
|---|---|---|
| N1 | Excluded from VAT | Items excluded by law (e.g., stamp duty) |
| N2 | Not subject to VAT | Operations outside the scope of VAT |
| N3 | Non-taxable | Exports and similar non-taxable operations |
| N4 | Exempt | VAT-exempt operations (e.g., medical services) |
| N5 | Margin scheme | Used goods sold under the margin scheme |
| N6 | Other non-VAT operations | Catch-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:
| Code | What went wrong | Can you retry? |
|---|---|---|
| 0100 | Non-compliant file (XML format error) | No -- fix the data and resubmit |
| 0200 | Validation error (missing/invalid fields) | No -- fix the data and resubmit |
| 0403 | Not authorized (certificate issue) | No -- check your seal certificate |
| 0429 | Rate limit exceeded (too many requests) | Yes -- Zyntem retries automatically |
| 0500 | AdE internal server error | Yes -- Zyntem retries automatically |
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
}
}'