Italy (FatturaPA / SdI)
Italian invoices must be sent as FatturaPA XML through the Sistema di Interscambio (SdI), run by Agenzia delle Entrate. Space Invoices builds the XML for every final invoice and credit note of an Italian entity, sends it to SdI, and shows SdI’s response on the document.
Prerequisites
| Requirement | Description |
|---|---|
| Italian entity | An entity with country_code: "IT", its VAT number and a complete legal address (with a five-digit postal code) |
| Fiscal regime | RF01 (ordinary) or RF19 (flat-rate, regime forfettario) |
Scope
| Supported | Not supported |
|---|---|
| Invoices and credit notes in EUR (private FPR12) | Public-administration invoices |
| Domestic and foreign, business and consumer buyers | Withholding, pension contributions and other special VAT procedures |
| RF01 and RF19 sellers | Other fiscal regimes |
Zero-VAT lines with natures N2.1, N2.2, N3.1 and N3.2 | Other domestic zero-VAT treatments, and more than one tax per line |
| Stamp duty (imposta di bollo) declaration | Fiscal currency conversion |
POS/RT linkage, PEM/PEL fiscal receipts and OSS/IOSS returns aren’t covered by this guide. Drafts can be saved without every compliance field; the checks apply when you issue.
Setup
Set the seller’s fiscal regime in settings.tax_rules.italy.fiscal_regime, and its codice fiscale in settings.tax_rules.italy.fiscal_code if it has one separate from the VAT number. Only choose the regime the seller actually uses.
await sdk.entities.update(entity.id, {
name: "Example Seller SRL",
address: "Via Milano 10",
city: "Milano",
post_code: "20100",
tax_number: "IT12345678903",
currency_code: "EUR",
is_tax_subject: true,
settings: {
tax_rules: { italy: { fiscal_regime: "RF01" } },
},
});Issuing invoices
Create invoices as usual. What you send for the buyer depends on who they are:
| Buyer | What to send |
|---|---|
| Italian business | VAT number (or, if the buyer has none, its fiscal code in customer.sdi.fiscal_code), plus the SdI recipient code or PEC in customer.sdi if you have one. Without either, the invoice goes to 0000000; give the buyer a courtesy copy and tell them it’s available through Agenzia delle Entrate. |
| Italian consumer | is_end_consumer: true and the buyer’s fiscal code in customer.sdi.fiscal_code. |
| Foreign business | The buyer’s VAT or tax ID in customer.tax_number (a company registration number doesn’t count). |
| Foreign consumer | Name and address only; no Italian fiscal code needed. |
Space Invoices picks the SdI routing (0000000, consumer or XXXXXXX for foreign buyers) itself. Seller and buyer both need a legal name, street address, city and country. customer.sdi is copied from the saved customer; to change it for one document, send the full customer with save_customer: false.
A domestic business with a recipient code:
const _invoice = await sdk.invoices.create({
"customer": {
"name": "Cliente Business SRL",
"country_code": "IT",
"address": "Via Roma 1",
"city": "Roma",
"post_code": "00100",
"tax_number": "IT10987654321",
"is_end_consumer": false,
"sdi": {
"recipient_code": "ABC1234"
}
},
"items": [
{
"name": "Subscription",
"quantity": 1,
"price": 100,
"taxes": [
{
"rate": 22
}
]
}
],
"currency_code": "EUR"
}, {
entity_id: entity.id,
request_id: "your-stable-order-operation-id",
});With no recipient code or PEC:
const _invoice = await sdk.invoices.create({
"customer": {
"name": "Cliente Business SRL",
"country_code": "IT",
"address": "Via Roma 1",
"city": "Roma",
"post_code": "00100",
"tax_number": "IT10987654321",
"is_end_consumer": false
},
"items": [
{
"name": "Subscription",
"quantity": 1,
"price": 100,
"taxes": [
{
"rate": 22
}
]
}
],
"currency_code": "EUR"
}, {
entity_id: entity.id,
request_id: "your-stable-order-operation-id",
});A domestic consumer. B2C doesn’t mean zero VAT; this example charges ordinary VAT:
const _invoice = await sdk.invoices.create({
"customer": {
"name": "Mario Rossi",
"country_code": "IT",
"address": "Via Roma 1",
"city": "Roma",
"post_code": "00100",
"is_end_consumer": true,
"sdi": {
"fiscal_code": "RSSMRA80A01H501U"
}
},
"items": [
{
"name": "In-app purchase",
"quantity": 1,
"price": 9.99,
"taxes": [
{
"rate": 22
}
]
}
],
"currency_code": "EUR"
}, {
entity_id: entity.id,
request_id: "your-stable-order-operation-id",
});Zero-VAT lines
A line with 0% VAT for a foreign buyer needs an explicit 0% tax and a nature with its legal_reference (1–100 characters):
nature | Use for | Typical legal reference |
|---|---|---|
N2.1 | Services outside the territorial scope of Italian VAT | Art. 7-ter DPR 633/72 |
N3.1 | Exports | Art. 8 DPR 633/72 |
N3.2 | Intra-community supplies of goods | Art. 41 D.L. 331/93 |
{ "e_invoicing": { "italy": { "nature": "N3.1", "legal_reference": "Art. 8 DPR 633/72" } }}This goes on each affected line in items[]. Choose the treatment that actually applies; a foreign buyer or a 0% rate alone doesn’t decide it. Taxable lines can’t carry a nature.
A service to a foreign business (N2.1):
const _invoice = await sdk.invoices.create({
"customer": {
"name": "Berlin Buyer GmbH",
"country_code": "DE",
"address": "Hauptstrasse 10",
"city": "Berlin",
"tax_number": "DE123456789",
"is_end_consumer": false
},
"items": [
{
"name": "Subscription",
"quantity": 1,
"price": 50,
"taxes": [
{
"rate": 0
}
],
"e_invoicing": {
"italy": {
"nature": "N2.1",
"legal_reference": "Art. 7-ter DPR 633/72"
}
}
}
],
"currency_code": "EUR"
}, {
entity_id: entity.id,
request_id: "your-stable-order-operation-id",
});Exported goods to a foreign consumer (N3.1):
const _invoice = await sdk.invoices.create({
"customer": {
"name": "Jane Consumer",
"country_code": "US",
"address": "100 Main Street",
"city": "Boston",
"is_end_consumer": true
},
"items": [
{
"name": "Exported goods",
"quantity": 1,
"price": 19.99,
"taxes": [
{
"rate": 0
}
],
"e_invoicing": {
"italy": {
"nature": "N3.1",
"legal_reference": "Art. 8 DPR 633/72"
}
}
}
],
"currency_code": "EUR"
}, {
entity_id: entity.id,
request_id: "your-stable-order-operation-id",
});Intra-community goods (N3.2):
const _invoice = await sdk.invoices.create({
"customer": {
"name": "Berlin Buyer GmbH",
"country_code": "DE",
"address": "Hauptstrasse 10",
"city": "Berlin",
"tax_number": "DE123456789",
"is_end_consumer": false
},
"items": [
{
"name": "Goods supplied within the EU",
"quantity": 1,
"price": 100,
"taxes": [
{
"rate": 0
}
],
"e_invoicing": {
"italy": {
"nature": "N3.2",
"legal_reference": "Art. 41 DL 331/93"
}
}
}
],
"currency_code": "EUR"
}, {
entity_id: entity.id,
request_id: "your-stable-order-operation-id",
});You can store the nature and reference on a catalog item as a default. Check it on each sale, since a different buyer may need a different treatment.
const zeroVat = await sdk.taxes.create({
name: "Export VAT 0%",
tax_rates: [{ rate: 0 }],
}, { entity_id: entity.id });
const _item = await sdk.items.create({
name: "Exported goods",
price: 19.99,
tax_ids: [zeroVat.id],
e_invoicing: {
italy: { nature: "N3.1", legal_reference: "Art. 8 DPR 633/72" },
},
}, { entity_id: entity.id });RF19 flat-rate sellers
RF19 sellers can’t charge VAT, so every line uses a 0% tax.
- Domestic buyer: leave out
e_invoicing.italy. Space Invoices adds natureN2.2with the statutory wording (“Operazione senza applicazione dell’IVA ai sensi dell’art. 1, commi da 54 a 89, Legge n. 190/2014”). To use different wording, sendnature: "N2.2"with your ownlegal_reference. - Foreign buyer: choose
N2.1,N3.1orN3.2like any other seller.N2.2isn’t allowed for foreign buyers or for RF01 sellers.
const _invoice = await sdk.invoices.create({
"customer": {
"name": "Cliente Business SRL",
"country_code": "IT",
"address": "Via Roma 1",
"city": "Roma",
"post_code": "00100",
"tax_number": "IT10987654321",
"is_end_consumer": false,
"sdi": {
"recipient_code": "ABC1234"
}
},
"items": [
{
"name": "Consulting session",
"quantity": 1,
"price": 45,
"taxes": [
{
"rate": 0
}
]
},
{
"name": "Follow-up review",
"quantity": 1,
"price": 40,
"taxes": [
{
"rate": 0
}
]
}
],
"currency_code": "EUR"
}, {
entity_id: entity.id,
request_id: "your-stable-order-operation-id",
});Stamp duty
When the N2.1 and N2.2 lines of a document total more than EUR 77.47 net, Space Invoices declares the EUR 2 stamp duty in the XML (DatiBollo). It isn’t added to the invoice total; the seller pays it separately. N3.x lines don’t count.
Drafts and credit notes
Drafts can leave out details you’ll add before issuing, but a line’s e_invoicing.italy needs both fields if you send it. Issuing runs the Italy checks and captures the seller settings.
const draft = await sdk.invoices.create({
is_draft: true,
currency_code: "EUR",
items: [{ name: "Subscription", quantity: 1, price: 100, taxes: [{ rate: 22 }] }],
}, { entity_id: entity.id, request_id: "your-stable-draft-operation-id" });
// After completing and reviewing the buyer, issue the saved draft.
await sdk.invoices.update(draft.id, {
customer: {
"name": "Cliente Business SRL",
"country_code": "IT",
"address": "Via Roma 1",
"city": "Roma",
"post_code": "00100",
"tax_number": "IT10987654321",
"is_end_consumer": false,
"sdi": {
"recipient_code": "ABC1234"
}
},
}, { entity_id: entity.id });
await sdk.documents.finalizeDocument(
draft.id, {}, { type: "invoice" }, { entity_id: entity.id },
);
const issued = await sdk.invoices.get(draft.id, undefined, { entity_id: entity.id });
console.log(issued.sdi?.status);Credit notes use the same buyer and line fields. Send positive quantities and amounts. To link a credit note to an invoice, see credit notes.
const _creditNote = await sdk.creditNotes.create({
"customer": {
"name": "Cliente Business SRL",
"country_code": "IT",
"address": "Via Roma 1",
"city": "Roma",
"post_code": "00100",
"tax_number": "IT10987654321",
"is_end_consumer": false,
"sdi": {
"recipient_code": "ABC1234"
}
},
"items": [
{
"name": "Subscription credit",
"quantity": 1,
"price": 25,
"taxes": [
{
"rate": 22
}
]
}
],
"currency_code": "EUR"
}, {
entity_id: entity.id,
request_id: "your-stable-credit-operation-id",
});Issued documents can’t be edited. Use a void or credit note instead; see Voids and corrections.
EU automation settings
settings.tax_rules.eu.auto_reverse_chargeapplies reverse charge to qualifying intra-EU B2B sales.settings.tax_rules.eu.auto_remove_tax_exportremoves all taxes on sales outside the EU. Italy needs an explicit tax on every line, so those invoices can’t be issued while it’s on.
Both are off by default. Custom documents keep the taxes you supply.
SdI status
Space Invoices sends each final invoice and credit note to SdI in the background, so creating a document doesn’t wait for SdI. Sandbox documents stay pending and never reach SdI.
const current = await sdk.invoices.get(invoice.id, undefined, { entity_id: entity.id });
console.log(current.sdi?.status);
console.log(current.sdi?.latest_submission_id);
// Creation or a __PROTECTED_0__ status does not establish SdI acceptance.
// Read the persisted receipt outcome before treating transmission as complete.The document’s sdi object shows where it stands. It’s managed by Space Invoices, so don’t send it when creating documents:
| Field | Meaning |
|---|---|
status | pending, submitted, accepted, delivered, rejected, non_deliverable, retry_scheduled or failed |
latest_submission_id, file_name, sdi_identifier | Identify the transmitted file |
last_error | Why SdI rejected the file or transmission failed, including the SdI code (for example EI01) |
next_retry_at | When the next automatic retry runs |
submitted_at, accepted_at, delivered_at, rejected_at, non_deliverable_at, failed_at | When each outcome happened |
diagnostics.requires_reconciliation | The outcome is unknown and needs checking; see below |
diagnostics.preparation_retry_count | Automatic retries so far (up to eight) |
diagnostics.certificate_warnings | Redacted channel warnings, such as a certificate nearing expiry |
Space Invoices retries configuration and signing failures, SdI’s EI02 (service unavailable) and connection failures before delivery automatically, up to eight times. XML validation errors, SdI’s EI01, EI03 and EI04, SOAP faults and certificate problems fail straight away, with the reason in last_error.
If a transmission may have reached SdI but no answer came back, sdi.diagnostics.requires_reconciliation is true and the submission is never resent automatically. Don’t create a replacement invoice; contact support, who confirm the outcome with SdI.
Voids and corrections
| Document state | Voiding an invoice | Voiding a credit note |
|---|---|---|
| Not yet sent, or rejected by SdI | Closes it locally. After a rejection, issue a new corrected invoice. | Closes it locally. |
| Accepted, delivered or non-deliverable | Issues a linked TD04 credit note that references the original invoice number and date. | Not possible. Issue a new invoice instead. |
| Waiting for SdI, or outcome unknown | Wait for SdI’s receipt. If sdi.diagnostics.requires_reconciliation is true, contact support. | Same as for invoices. |
Once SdI accepts the TD04 credit note created by a void, it’s locked, and the invoice it offsets stays voided.
For an invoice issued before Space Invoices transmitted to SdI, the original transmission evidence has to be recovered before you can void it; contact support. Credit notes without a transmission record can’t be voided.
If voiding an invoice from an integration order issues the credit note but the refund step then fails, the credit note stays in place. Don’t create another one, and don’t rely on voided_at alone to decide the void completed; contact support.
Italy calculates VAT on each rate’s combined taxable base and spreads the VAT cents across the lines. When you enter gross prices, a small difference is shown as payable rounding so the gross total stays as entered, within the usual EUR 1 limit.
Web and embedded forms
The invoice and credit-note forms guide users through the same rules:
- The SdI fields (recipient code, PEC, fiscal code) only show for Italian buyers.
- New lines for a foreign buyer default to 0% with the matching nature and legal reference:
N2.1for an intra-EU business,N3.1outside the EU. An intra-EU consumer keeps the ordinary Italian VAT rate for an RF01 seller; only an RF19 seller getsN2.1there. Lines you’ve edited are never changed, and switching the buyer back to Italy restores the domestic tax on untouched lines. - Choosing a nature fills in its standard legal reference, unless you’ve typed your own.
- If a buyer’s EU VAT number fails VIES, the sale is treated as B2C with standard VAT. If VIES is unavailable, reverse charge stays and a warning is shown.
- A notice appears when stamp duty applies, and the PDF prints it too.
- Document lists show an SdI status badge. A document voided before sending shows as cancelled.
Common errors
Italy validation errors return 422 with a message explaining the fix. The most common:
| Problem | Fix |
|---|---|
| Missing entity VAT number or fiscal regime | Complete the entity setup. |
| Incomplete seller or buyer address | Add street, city, country and, in Italy, a five-digit postal code. |
| Domestic consumer without a fiscal code | Add customer.sdi.fiscal_code. |
| Foreign business without a VAT or tax ID | Add customer.tax_number. |
| Invalid recipient code | Use the buyer’s 7-character SdI code. |
| Zero-VAT line without a nature, or an unsupported treatment | Add a supported nature and legal_reference, with an explicit 0% tax. |
Line without an explicit tax, including when auto_remove_tax_export removed it on a non-EU sale | Add an explicit tax (0% for zero VAT), or turn the setting off. |
| Multiple taxes on one line | Use one tax per line. |
VAT on an RF19 line, a nature on a taxed line, or N2.2 outside a domestic RF19 sale | Use 0% for RF19, remove the nature from taxed lines, and use N2.1, N3.1 or N3.2 for foreign buyers. |
| Missing buyer country, or invalid PEC | Add customer.country_code, or correct the PEC address. |
Related API
See the Invoices API Reference, Credit Notes API Reference and Entities API Reference.