Skip to content

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

RequirementDescription
Italian entityAn entity with country_code: "IT", its VAT number and a complete legal address (with a five-digit postal code)
Fiscal regimeRF01 (ordinary) or RF19 (flat-rate, regime forfettario)

Scope

SupportedNot supported
Invoices and credit notes in EUR (private FPR12)Public-administration invoices
Domestic and foreign, business and consumer buyersWithholding, pension contributions and other special VAT procedures
RF01 and RF19 sellersOther fiscal regimes
Zero-VAT lines with natures N2.1, N2.2, N3.1 and N3.2Other domestic zero-VAT treatments, and more than one tax per line
Stamp duty (imposta di bollo) declarationFiscal 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.

Configure the Italian sellertypescript
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:

BuyerWhat to send
Italian businessVAT 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 consumeris_end_consumer: true and the buyer’s fiscal code in customer.sdi.fiscal_code.
Foreign businessThe buyer’s VAT or tax ID in customer.tax_number (a company registration number doesn’t count).
Foreign consumerName 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:

Domestic business with recipient codetypescript
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:

Domestic business without routing detailstypescript
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:

Domestic consumer with fiscal codetypescript
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):

natureUse forTypical legal reference
N2.1Services outside the territorial scope of Italian VATArt. 7-ter DPR 633/72
N3.1ExportsArt. 8 DPR 633/72
N3.2Intra-community supplies of goodsArt. 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):

Foreign business: territorial exclusion exampletypescript
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):

Foreign consumer: exported goods exampletypescript
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):

Eligible intra-community supply of goodstypescript
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.

Save an item with an explicit export treatmenttypescript
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 nature N2.2 with 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, send nature: "N2.2" with your own legal_reference.
  • Foreign buyer: choose N2.1, N3.1 or N3.2 like any other seller. N2.2 isn’t allowed for foreign buyers or for RF01 sellers.
RF19 flat-rate invoice above the stamp-duty thresholdtypescript
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.

Save a draft and issue it after reviewtypescript
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.

Create an Italian credit notetypescript
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_charge applies reverse charge to qualifying intra-EU B2B sales.
  • settings.tax_rules.eu.auto_remove_tax_export removes 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.

Read persisted SdI statustypescript
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:

FieldMeaning
statuspending, submitted, accepted, delivered, rejected, non_deliverable, retry_scheduled or failed
latest_submission_id, file_name, sdi_identifierIdentify the transmitted file
last_errorWhy SdI rejected the file or transmission failed, including the SdI code (for example EI01)
next_retry_atWhen the next automatic retry runs
submitted_at, accepted_at, delivered_at, rejected_at, non_deliverable_at, failed_atWhen each outcome happened
diagnostics.requires_reconciliationThe outcome is unknown and needs checking; see below
diagnostics.preparation_retry_countAutomatic retries so far (up to eight)
diagnostics.certificate_warningsRedacted 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 stateVoiding an invoiceVoiding a credit note
Not yet sent, or rejected by SdICloses it locally. After a rejection, issue a new corrected invoice.Closes it locally.
Accepted, delivered or non-deliverableIssues 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 unknownWait 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.1 for an intra-EU business, N3.1 outside the EU. An intra-EU consumer keeps the ordinary Italian VAT rate for an RF01 seller; only an RF19 seller gets N2.1 there. 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:

ProblemFix
Missing entity VAT number or fiscal regimeComplete the entity setup.
Incomplete seller or buyer addressAdd street, city, country and, in Italy, a five-digit postal code.
Domestic consumer without a fiscal codeAdd customer.sdi.fiscal_code.
Foreign business without a VAT or tax IDAdd customer.tax_number.
Invalid recipient codeUse the buyer’s 7-character SdI code.
Zero-VAT line without a nature, or an unsupported treatmentAdd 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 saleAdd an explicit tax (0% for zero VAT), or turn the setting off.
Multiple taxes on one lineUse one tax per line.
VAT on an RF19 line, a nature on a taxed line, or N2.2 outside a domestic RF19 saleUse 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 PECAdd customer.country_code, or correct the PEC address.

See the Invoices API Reference, Credit Notes API Reference and Entities API Reference.