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:
Sending — B2B and B2C
fiskaly generates the FatturaPA and submits it to SDI, which validates and forwards it to the recipient asynchronously. No network registration is required. Business and consumer recipients each take different fields.
Receiving — B2B only
Optional, and enabled per Taxpayer. Requires a one-time registration to receive invoices from your suppliers.
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.
If you already have a SIGN IT integration, you don’t start from scratch — you extend your existing Taxpayer with the additional registration data and commission an E_INVOICE_SERVICE System. See E-INVOICE IT for SIGN IT customers for the full steps.
Prerequisites
Section titled “Prerequisites”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.
E-INVOICE IT currently supports Taxpayers of type COMPANY. Onboarding an INDIVIDUAL Taxpayer — a freelancer or sole trader issuing under their own codice fiscale rather than a company VAT ID — is coming soon.
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,RMentry— 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_SHAREHOLDERorMULTIPLE_SHAREHOLDERSliquidation_status—IN_LIQUIDATIONorNOT_IN_LIQUIDATIONtax_regime— optional,ORDINARY(default) orFLAT_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 individualcontent.address— registered legal addresscontent.address.region— Provincia (Province code, e.g.MI,RM); required on the Taxpayer and validated at commissioning
For e-invoicing in Italy, content.fiscalization.registration and content.address.region (Provincia) are required fields on the Taxpayer. They are validated when the E_INVOICE_SERVICE System is commissioned — commissioning fails if they are missing. Make sure both fields are set or updated on the Taxpayer beforehand.
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:
Business recipients (B2B)
Routed by the recipient's SDI destination code, or by PEC if they have no SDI inbox.
Consumer recipients (B2C)
Italian residents are identified by codice fiscale; consumers abroad by a foreign identifier.
Every recipient carries invoicing.type = SDI. What changes between the three cases is the identification, the destination code, and whether a PEC is involved:
| Business | Consumer in Italy | Consumer abroad | |
|---|---|---|---|
type | BUSINESS | CONSUMER | CONSUMER |
identification | VAT — validated | TAX — codice fiscale, validated | any type; not validated |
invoicing.destination_code | the recipient’s 7-character code, or "0000000", or "XXXXXXX" | "0000000" | "XXXXXXX" |
invoicing.pec | required with "0000000" | optional | not used |
address.region | required when the address is in Italy | required | not required |
name | company name | gender, forename, surname | gender, forename, surname |
- The 7-character code the recipient gave you (e.g.
ABC1234) — uppercase letters and digits only. "0000000"— the recipient has no SDI inbox: every consumer, plus businesses that receive by PEC."XXXXXXX"— the recipient is abroad.
Business recipients (B2B)
Section titled “Business recipients (B2B)”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:
| Scenario | destination_code | pec |
|---|---|---|
| Recipient has a registered SDI inbox | their 7-character code (e.g. ABC1234) | optional |
| Recipient is not registered with SDI | "0000000" | required |
| Recipient is outside Italy | "XXXXXXX" | not used |
A pec is a Posta Elettronica Certificata address — Italy’s certified email. SDI checks the domain, so an ordinary mailbox is rejected.
"0000000" means deliver by PEC, which is why the pec is mandatory with it for a business recipient.
recipients[].address.region (the recipient’s Provincia) isn’t marked required in the shared Unified API schema, but Italy requires it whenever the address is in Italy. It is not checked when the record is created: createRecord returns successfully and the transmission fails later, taking the whole chain to FAILED. Set it up front rather than discovering it asynchronously.
Consumer recipients (B2C)
Section titled “Consumer recipients (B2C)”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.
Consumers resident in Italy
Section titled “Consumers resident in Italy”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.
For a consumer resident in Italy an identification is mandatory, it must be of type TAX, and the codice fiscale is checked when you create the record — a missing or malformed one is rejected there and no invoice is created. SDI validates it again against the tax registry, so a well-formed but unknown code is rejected at that stage.
Consumers outside Italy
Section titled “Consumers outside Italy”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.
| Field | Value |
|---|---|
recipients[].identification | required; VAT, TAX, PASSPORT, DOCUMENT, or OTHER — whichever fits the identifier you hold |
recipients[].address.country | the 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.
"XXXXXXX" tells SDI that clearance is requested but that delivery through the exchange system does not apply — SDI has no channel to reach a recipient abroad. Send the consumer their copy of the invoice (PDF or equivalent) separately, outside SDI.
gender is required by the Unified API, not by SDI — the FatturaPA invoice carries no such field. Send DIVERSE when you don’t have the data.
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.
We are building a supported path that links a fiscalized receipt and an e-invoice to one another, so that a single supply carries one invoice and one audit trail. Until it is available, capture the invoice request before the transaction is closed.
Handling SDI responses
Section titled “Handling SDI responses”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.
SDI outcomes typically arrive within a few minutes, though the SDI specification allows up to 48 hours.
All three Records reach their final state together:
| Record | Final state |
|---|---|
E_INVOICE::TRANSMISSION | COMPLETED or FAILED, mode=FINISHED |
TRANSACTION::INVOICE | COMPLETED or FAILED, mode=FINISHED |
INTENTION::TRANSACTION | COMPLETED 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.
Reading the outcome
Section titled “Reading the outcome”state tells you where an invoice stands, logs[] tells you why. Read them together and act on what you find:
| What you see | What it means | What to do |
|---|---|---|
4xx from createRecord, no Record created | The payload was rejected. Nothing was created and nothing was sent. | Fix the payload and retry on the same INTENTION::TRANSACTION. |
state=ACCEPTED | Still in progress. | Poll the E_INVOICE::TRANSMISSION Record. Outcomes usually arrive within minutes. |
state=FAILED | Finished, 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 entries | SDI 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.
It does not mean the recipient received it. SDI reports both delivery (RC) and non-delivery (MC, which is still legally valid) as success, and the API does not distinguish the two.
Where validation failures surface
Section titled “Where validation failures surface”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 fails | Example | What you see |
|---|---|---|
| Schema constraints | identification.number outside AlphaNumerical28; document.number outside ^[0-9A-Z_/\-\.]{1,20}$ | 400 from createRecord. Nothing is created. |
| Field validation | A codice fiscale with a bad format or a wrong check digit | createRecord 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. |
| Downstream | A missing recipients[].address.region | createRecord succeeds and the chain later goes to FAILED. |
The middle row is the one that catches integrators out: a COMPLETED / FINISHED Record with no invoice behind it looks exactly like a successful one if you read state and stop there. Always read content.logs for ERROR entries, and check that content.used_in is present, before you treat an invoice as issued.
Failures and resubmission
Section titled “Failures and resubmission”If SDI returns NS (Notifica di Scarto), the invoice is legally non-existent:
- Read
logs[].messageon any of the three Records to get the SDI rejection reason. - Create a new
INTENTION::TRANSACTIONand a newTRANSACTION::INVOICEwith corrected data. - The same
document.numbermay be reused within 5 days of theNSrejection. - The failed chain remains
FAILEDpermanently — it is kept for audit purposes.
Each resubmission starts a fresh transaction chain — UAPI treats it as an entirely new submission.
Receiving e-invoices
Section titled “Receiving e-invoices”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
JKKZDGRas 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.
TRANSMISSION_RECEPTION means the System is able to receive, but not that inbound invoices are actually being delivered to the Taxpayer. That delivery requires the AdE registration: until it is in place, the Taxpayer receives no invoices, even while the System is ready. You can complete the AdE registration at any time — for example, to enable receiving on a Taxpayer that was previously set up for sending only.
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.
If retrieveSystem keeps returning compliance.state = TRANSMISSION_ONLY after commissioning, the Taxpayer’s registration didn’t complete. Treat it as a stuck transition and contact the fiskaly support team at dev-support@fiskaly.com with the Taxpayer’s VAT ID so we can investigate.
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 arrives
Section titled “When an invoice arrives”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.
Retrieving received invoices
Section titled “Retrieving received invoices”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:
- Call
listRecordsand filter forE_INVOICE::RECEPTION. - For each new Record, call
retrieveRecordwithcompliance-artifactto get the signed XML.
listRecords returns the most recent Records (limit defaults to 10, newest first), so track what you’ve already processed.
If you are integrating E-INVOICE IT without SIGN IT, please reach out to the fiskaly support team at dev-support@fiskaly.com — we will guide you through the Taxpayer setup for your specific case.
Archiving
Section titled “Archiving”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.
Choosing the response format
Section titled “Choosing the response format”Both artifacts honour the Accept request header, which decides the shape of the response:
Accept | What you get |
|---|---|
application/xml | The artifact itself — the FatturaPA XML, or the preservation-receipt XML. |
application/json | The Record as JSON, with the artifact Base64-encoded in the usual content field. |
The spec’s RecordResponse declares only application/json, so the XML variant does not show up in the reference. It is nonetheless supported, and it is what the Postman collection uses.
When artifacts become available
Section titled “When artifacts become available”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 state | What it tells you about the artifacts |
|---|---|
state: ACCEPTED, mode: PROCESSING | Not terminal. Artifacts may still be missing, and missing means not yet — they are still coming. |
state: COMPLETED / FAILED / REJECTED, mode: FINISHED | Terminal. Whatever is present is all there will be; a missing artifact will not appear later. |
Requesting an artifact that does not exist yet returns 200 with the plain Record — no error, and no “not ready” signal of any kind. Decide from the Record’s state and mode whether an absent artifact means not yet or never, rather than from the response to the artifact request.
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-artifactThe 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.
file-artifact returns the signed envelope of the Record as submitted — a generic wrapper used for all Record types, fixed at record creation and unconnected to FatturaPA generation. It is well-formed XML, but it is not the FatturaPA and must not be recorded as the invoice. For the FatturaPA, use compliance-artifact.
The preservation receipt is not a rendering of the invoice, and neither is file-artifact. For a human-readable rendering, download the transmission’s file bundle — a ZIP containing the FatturaPA XML, the same document as a PDF, and a metadata JSON — from its own endpoint:
GET /files/{transmissionId}.zipIn the test environment archiving runs end to end, but the result has no legal validity — it is for integration testing only. Only invoices processed in live are certified as legally preserved.
Stamp duty (imposta di bollo)
Section titled “Stamp duty (imposta di bollo)”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-artifactThe XML arrives Base64-encoded, so decode it before looking for DatiBollo.
Expect two total-mismatch warnings
Section titled “Expect two total-mismatch warnings”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.
This is how it works today. The handling of stamp duty in the totals is being reworked (META-5229), after which these warnings will no longer be raised.
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.