Skip to content

Italy — E-INVOICE IT (Unified API)

E-invoicing in Italy is mandatory, regulated by the Agenzia delle Entrate, and has been legally required since January 2019. Every invoice, B2B and B2C, is exchanged through SDI (Sistema di Interscambio) as FatturaPA XML and must be preserved long-term through certified archiving (conservazione a norma).

E-INVOICE IT handles that exchange for you. You send invoice data through the Unified API; fiskaly builds the FatturaPA XML, transmits it to SDI, tracks the outcome, and preserves the invoice for the legally required 10 years.

fiskaly issues the Standard Invoice (Fattura, TD01) and the Credit Note (Nota di credito, TD04) — between them, ordinary invoicing and corrections. You don’t set the document type: it is derived from the Unified API operation you call. The Simplified Invoice (Fattura semplificata, TD07) is on the roadmap.

Document types outside the current scope

Advance invoices (TD02), deferred invoices (TD24, TD25), and invoices to public administrations (B2G, the FPA12 FatturaPA variant) are not emitted. recipients[].type accepts BUSINESS and CONSUMER.

This page covers the Italy-specific requirements that complement the General Step-by-Step Integration, for onboarding a Taxpayer for e-invoicing.

fiskaly handles both directions of the SDI exchange, and the two work differently:

On top of both directions, every invoice you send and receive is automatically archived for you. This means certified long-term preservation (conservazione a norma) for the legally required 10 years, with no setup on your side.

These apply to any Taxpayer you onboard for e-invoicing in Italy — whether you send, receive, or both. Make sure you have the following Taxpayer information ready before proceeding.

Italy-specific registration data — submitted under content.fiscalization.registration:

  • company_id — Numero Registro Imprese (Business Register number)
  • office — province code of the Camera di Commercio that issued the REA number, e.g. MI, RM
  • entry — REA number (6 or 7 digits)
  • legal_form — legal form of the entity (e.g. LIMITED_LIABILITY_COMPANY, JOINT_STOCK_COMPANY)
  • capital — registered share capital in EUR (e.g. "10000.00")
  • shareholder_status — SOLE_SHAREHOLDER or MULTIPLE_SHAREHOLDERS
  • liquidation_status — IN_LIQUIDATION or NOT_IN_LIQUIDATION
  • tax_regime — optional, ORDINARY (default) or FLAT_RATE_SCHEME (Regime Forfettario)

Standard Taxpayer fields — part of the Taxpayer itself (shared with SIGN IT if you use it):

  • content.name — registered legal name of the company or individual
  • content.address — registered legal address
  • content.address.region — Provincia (Province code, e.g. MI, RM); required on the Taxpayer and validated at commissioning

Sending e-invoices: recipient requirements

Section titled “Sending e-invoices: recipient requirements”

To send an e-invoice, you specify its recipient and their routing details — right on the invoice record. When you create the invoice (TRANSACTION::INVOICE) on the E_INVOICE_SERVICE System with createRecord, add the recipient to its recipients array. Which fields you set depends on who you are invoicing:

Every recipient carries invoicing.type = SDI. What changes between the three cases is the identification, the destination code, and whether a PEC is involved:

BusinessConsumer in ItalyConsumer abroad
typeBUSINESSCONSUMERCONSUMER
identificationVAT — validatedTAX — codice fiscale, validatedany type; not validated
invoicing.destination_codethe recipient’s 7-character code, or "0000000", or "XXXXXXX""0000000""XXXXXXX"
invoicing.pecrequired with "0000000"optionalnot used
address.regionrequired when the address is in Italyrequirednot required
namecompany namegender, forename, surnamegender, forename, surname

Each business recipient is a BUSINESS with a populated invoicing block, as the General Step-by-Step Integration explains. Beyond the standard details every e-invoice requires (recipient name, address, tax identification, invoice lines, and so on), Italy adds the SDI routing fields above.

Which values destination_code and pec take depends on the recipient:

Scenariodestination_codepec
Recipient has a registered SDI inboxtheir 7-character code (e.g. ABC1234)optional
Recipient is not registered with SDI"0000000"required
Recipient is outside Italy"XXXXXXX"not used

When your customer is a private individual rather than a business, set recipients[].type to CONSUMER. The invoice goes through SDI as a FatturaPA (TD01) document, like a B2B invoice.

Consumers have no SDI inbox, so routing works differently. How you identify the consumer depends on whether they are resident in Italy:

  • Resident in Italy — identified by their codice fiscale, routed with destination code "0000000".
  • Outside Italy — no codice fiscale exists, so a foreign identifier takes its place and the invoice is routed with "XXXXXXX".

Beyond the fields in the table above, a consumer recipient always carries name.gender, name.forename, and name.surname, plus the consumer’s address.

Send the codice fiscale as a TAX identification (16 characters), the residence address including region (Provincia), and destination code "0000000". A pec may be added but is not required.

A consumer who isn’t resident in Italy has no codice fiscale, so send a foreign identifier in its place — typically a foreign tax or VAT number.

FieldValue
recipients[].identificationrequired; VAT, TAX, PASSPORT, DOCUMENT, or OTHER — whichever fits the identifier you hold
recipients[].address.countrythe consumer’s country as an ISO 3166-1 alpha-2 code
recipients[].address.code"00000" — FatturaPA only accepts a 5-digit postal code
recipients[].invoicing.destination_code"XXXXXXX"

The identifier travels to SDI as the IdCodice; neither fiskaly nor SDI checks it for validity once the address country is not Italy. region is not required for a non-Italian address.

For a consumer resident in Italy, SDI files the original invoice in their reserved area on the AdE portal (Fatture e Corrispettivi).

When the customer needs an invoice instead of a receipt

Section titled “When the customer needs an invoice instead of a receipt”

Italy reports the two documents through separate channels — the receipt through corrispettivi, the invoice through SDI — and nothing links them automatically.

Capture the request before the sale is closed. When the customer tells you they need a fattura, issue a TRANSACTION::INVOICE in place of a TRANSACTION::RECEIPT. Asking “do you need an invoice?” as part of checkout is the flow fiskaly supports end to end today, whether the customer asks before or during the sale.

Unlike a synchronous API call, the SDI outcome is asynchronous. After you create the invoice (TRANSACTION::INVOICE) with createRecord, poll the E_INVOICE::TRANSMISSION Record to learn whether SDI accepted it. There is no webhook.

All three Records reach their final state together:

RecordFinal state
E_INVOICE::TRANSMISSIONCOMPLETED or FAILED, mode=FINISHED
TRANSACTION::INVOICECOMPLETED or FAILED, mode=FINISHED
INTENTION::TRANSACTIONCOMPLETED or FAILED, mode=FINISHED

On failure, the SDI rejection reason is available in logs[].message on all three Records. For more details, see How to check the status of an e-invoice on our Support page.

state tells you where an invoice stands, logs[] tells you why. Read them together and act on what you find:

What you seeWhat it meansWhat to do
4xx from createRecord, no Record createdThe payload was rejected. Nothing was created and nothing was sent.Fix the payload and retry on the same INTENTION::TRANSACTION.
state=ACCEPTEDStill in progress.Poll the E_INVOICE::TRANSMISSION Record. Outcomes usually arrive within minutes.
state=FAILEDFinished, and the invoice is not legally valid. It was either stopped on the way to SDI or rejected by SDI with NS.Read logs[].message for the reason, correct the data, and start a new chain — see Failures and resubmission.
state=COMPLETED with an ERROR entry in logs[]Something went wrong along the way without stopping the invoice.Read logs[].message before you treat the invoice as sent.
state=COMPLETED, no ERROR entriesSDI accepted the invoice.Nothing further.

Each entry in logs[] is either an ERROR or a WARNING. A WARNING is informational and never blocks an invoice — you get one when something in your payload was adjusted or didn’t add up, for example if you sent several outstanding payments and only the first one was used, or if the VAT totals in your payload differ from the totals calculated from the invoice lines.

Not every rejection looks like a rejection. Invoice data is checked at three different points, and each one fails in a different shape:

Where it failsExampleWhat you see
Schema constraintsidentification.number outside AlphaNumerical28; document.number outside ^[0-9A-Z_/\-\.]{1,20}$400 from createRecord. Nothing is created.
Field validationA codice fiscale with a bad format or a wrong check digitcreateRecord succeeds. The Record closes COMPLETED / FINISHED, but content.used_in is absent and nothing is transmitted. The reason appears only as a content.logs entry with severity ERROR.
DownstreamA missing recipients[].address.regioncreateRecord succeeds and the chain later goes to FAILED.

If SDI returns NS (Notifica di Scarto), the invoice is legally non-existent:

  1. Read logs[].message on any of the three Records to get the SDI rejection reason.
  2. Create a new INTENTION::TRANSACTION and a new TRANSACTION::INVOICE with corrected data.
  3. The same document.number may be reused within 5 days of the NS rejection.
  4. The failed chain remains FAILED permanently — it is kept for audit purposes.

An E_INVOICE_SERVICE System can both send and receive e-invoices. Receiving is optional — a send-only Taxpayer can skip this section.

Two separate, independent things are involved:

  • Readiness to receive — fiskaly sets this up automatically when you commission the System.
  • SDI recipient-code registration (manual, done by you) — in your Agenzia delle Entrate (AdE) portal, set fiskaly’s SDI recipient code JKKZDGR as your company’s SDI destination, so SDI routes your inbound invoices to fiskaly.

The commissioning response shows compliance.state as TRANSMISSION_ONLY. It then moves to TRANSMISSION_RECEPTION automatically once the Taxpayer’s registration completes — which it normally does, regardless of whether you’ve done the AdE registration or plan to receive.

Call retrieveSystem at any time to read the current compliance.state, which reflects only readiness — not the AdE registration:

  • TRANSMISSION_RECEPTION — fiskaly has set the System up to receive.
  • TRANSMISSION_ONLY — that setup hasn’t completed.

There is nothing extra to set up for routing: fiskaly automatically matches each incoming invoice to the right Taxpayer using its tax ID — which is also why each tax ID must belong to a single registered Taxpayer. You don’t need to configure or store a separate routing code.

When an invoice is sent to you through SDI, fiskaly receives it automatically:

  • the inbound delivery is processed automatically on our side
  • a reception record is created for the invoice
  • the signed XML, PDF, and metadata are stored against the record
  • duplicate deliveries are deduplicated

In practice, the first invoice that reaches you this way is also your confirmation that receiving is set up correctly.

Every invoice addressed to your Taxpayer is received, validated, and stored automatically as a Record — there’s no webhook, so you poll for new ones:

  1. Call listRecords and filter for E_INVOICE::RECEPTION.
  2. For each new Record, call retrieveRecord with compliance-artifact to get the signed XML.

listRecords returns the most recent Records (limit defaults to 10, newest first), so track what you’ve already processed.

Italian law requires e-invoices to be preserved long-term through certified digital archiving (conservazione a norma): they must be kept for at least 10 years and stored so they remain immutable, authentic, and easily retrievable, with digital signatures and timestamps (marca temporale). Keeping your own copy of the XML is not enough — fiskaly handles this for you.

For Italy, archiving is automatic and always on: every invoice you send and every invoice you receive through SDI is legally preserved, with no extra setup or API call on your side. It only relies on the Taxpayer’s Italian tax ID, which is already part of onboarding. Each preserved invoice gets a preservation receipt as proof of conservazione a norma. Preservation is event-driven and usually completes within seconds; the receipt itself can take up to roughly six minutes to be issued, after which fiskaly attaches it automatically — nothing is required on your side.

Retrieve an e-invoice’s artifacts from its record with retrieveRecord, selecting the artifact through a query parameter:

  • compliance-artifact — the legally authoritative document. For Italy, the SDI-validated FatturaPA XML.
  • archive-artifact — the preservation receipt attesting legal preservation (conservazione a norma), returned as a signed XML.

Both artifacts honour the Accept request header, which decides the shape of the response:

AcceptWhat you get
application/xmlThe artifact itself — the FatturaPA XML, or the preservation-receipt XML.
application/jsonThe Record as JSON, with the artifact Base64-encoded in the usual content field.

compliance-artifact and archive-artifact do not exist for the whole life of a Record. Neither is produced at record creation: both are written once the transmission completes — that is, once SDI has responded, which the SDI specification allows to take up to 48 hours. They settle at the same moment, because the archiving step runs before the Record’s state changes.

That gives you a rule based on state alone, with nothing to time:

Record stateWhat it tells you about the artifacts
state: ACCEPTED, mode: PROCESSINGNot terminal. Artifacts may still be missing, and missing means not yet — they are still coming.
state: COMPLETED / FAILED / REJECTED, mode: FINISHEDTerminal. Whatever is present is all there will be; a missing artifact will not appear later.

The preservation receipt belongs to the E_INVOICE::TRANSMISSION Record rather than the TRANSACTION::INVOICE. Take content.used_in.id from the invoice Record, then:

GET /records/{transmissionId}?archive-artifact

The receipt is returned Base64-encoded in content.compliance.archive.data. Invoices you send and invoices you receive each have their own receipt. While the artifact is not yet available, the field is absent from the response rather than present and empty.

Italian law requires a €2.00 stamp duty (imposta di bollo) on invoices whose VAT-exempt lines exceed €77.47 in total. fiskaly applies it automatically: when the total of the qualifying VAT-exempt lines on an invoice passes that threshold, the stamp duty is added to the transmitted FatturaPA.

There is no stamp duty field in the request or the response — nothing to set when you create the invoice, and nothing returned on the Record. The only place the applied value appears is the DatiBollo element of the FatturaPA XML, which you can read from the transmitted document:

GET /records/{transmissionId}?compliance-artifact

The XML arrives Base64-encoded, so decode it before looking for DatiBollo.

The €2.00 is added after the invoice totals have been calculated, so on every invoice that crosses the threshold the transmitted totals are €2.00 higher than the ones you sent. The Record records that difference as two WARNING entries in content.logs, for example:

provided '100.00000000', calculated '102.00'

This is expected, and it is the signal that stamp duty was applied. Keep sending your pre-duty totals: there is no payload that both carries the stamp duty and avoids the warnings. Treat these two WARNING entries as informational and do not fail your integration on them.

Crediting an invoice issued outside fiskaly

Section titled “Crediting an invoice issued outside fiskaly”

In Italy the document type is the FatturaPA type identifier: a CORRECTION is transmitted as TD04 (Nota di credito), an INVOICE as TD01 (Fattura). Because the type is derived from the operation, an invoice that fiskaly did not transmit cannot be credited — issuing a TD01 that references the earlier invoice raises the amount owed instead of cancelling it. See Credit Note for the general rule.

For one-off cases, SdI grants portal access to all merchants, where a credit note can be issued manually. This does not scale and is not an integration pattern — treat it as a migration fallback, and keep the previous e-invoicing system available until everything it issued has been settled or credited.