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 ordinaria, 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) with createRecord, on the E_INVOICE_SERVICE System or, if the Taxpayer also uses SIGN IT, on its FISCAL_DEVICE, 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"; "XXXXXXX" abroad"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 a business outside Italy"XXXXXXX"not used

An invoice to a business abroad is reported to SDI but not delivered through it. Send it with destination_code: "XXXXXXX" and the recipient’s foreign VAT number:

FieldValue
recipients[].identificationVAT — the recipient’s foreign VAT number
recipients[].address.countrythe recipient’s country as an ISO 3166-1 alpha-2 code (not IT)
recipients[].address.codethe recipient’s real postal code. fiskaly writes "00000" into the FatturaPA, which only takes Italian postal codes; the PDF copy shows the real one
recipients[].invoicing.destination_code"XXXXXXX"

region is not required and pec is not used. A business based in Italy always takes the domestic flow above.

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 normally 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) as two uppercase letters, for example RM, and destination code "0000000". A pec is optional.

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.codethe recipient’s real postal code. fiskaly writes "00000" into the FatturaPA, which only takes Italian postal codes; the PDF copy shows the real one
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).

A credit note for a consumer invoice follows the same steps as for a business invoice: an INTENTION::TRANSACTION, then a TRANSACTION::CORRECTION that references the original invoice.

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.

If the invoice is transmitted, 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, read logs[].message: a failure before the invoice reaches SDI is logged on the TRANSACTION::INVOICE Record, an SDI rejection on the E_INVOICE::TRANSMISSION Record, from where it is also copied to the logs of the TRANSACTION::INVOICE and INTENTION::TRANSACTION 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 about every 15 seconds until mode=FINISHED. Outcomes usually arrive within minutes; a rejection can take up to 5 days.
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 digit; a missing recipients[].address.regioncreateRecord 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.
DownstreamSDI rejects the invoice with NS, or the transmission fails on the way to SDIcreateRecord succeeds and the chain later goes to FAILED. FAILED covers every failed transmission, not only an NS — the reason is in logs[].message.

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

  1. Read logs[].message to get the SDI rejection reason. It is on the E_INVOICE::TRANSMISSION Record and is also copied to the logs of the TRANSACTION::INVOICE and INTENTION::TRANSACTION Records.
  2. Create a new INTENTION::TRANSACTION and a new TRANSACTION::INVOICE with corrected data.
  3. A new 5-day window starts with the NS rejection. Within it, you can resend the invoice with the same document.number, as new Records with a new idempotency key.
  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, attached automatically once it’s ready — nothing is required on your side. Rather than a fixed delay, use the Record’s state and mode to know whether the receipt has settled; see When artifacts become available below.

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 are returned inside the Record’s JSON response, as an object carrying type and data:

"compliance": {
"artifact": { "type": "application/xml", "data": "<base64>" }
}

For Italy, type is always application/xml — there is no separate raw-XML response and no PDF variant of either artifact. The Accept header has no effect on this.

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 can take up to 5 days. 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.

For an unpaid invoice (payments[].type = OUTSTANDING), the instruction object is optional. Without it, the invoice is accepted and transmitted, but the payment method is reported as a bank transfer without any account details, so your customer receives an e-invoice with no IBAN to pay to.

If the invoice should be paid by bank transfer, send instruction.type = CREDIT_TRANSFER with:

  • account: your IBAN
  • name: the account holder, i.e. you, the invoice issuer
  • payment_service_provider: your bank’s BIC/SWIFT code (a bank name is not accepted)

For a full example and the direct-debit variant, see How to represent payments on an invoice.

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.