Skip to content
Log in

SDI (FatturaPA)

The SDI (Sistema di Interscambio) is Italy’s mandatory e-invoicing platform, run by the Agenzia delle Entrate. All invoices between Italian operators —B2B, B2C and B2G— must be issued and received through the SDI in FatturaPA format (the Italian national structured XML format).

B2Brouter’s REST API generates the FatturaPA XML, transmits it to the SDI and retrieves the acknowledgements, so your integration can focus on business logic.

The SDI is the central hub every Italian electronic invoice passes through. In practice:

  • Invoices are issued in FatturaPA 1.2 format (xml.fatturapa.1.2), an Italian XML format.
  • The SDI validates the document against the official schema (XSD) and delivers it to the recipient.
  • Each accepted invoice receives a registration identifier (IdentificativoSdI).
  • If the recipient cannot receive the document electronically, the SDI makes it available in their cassetto fiscale.

E-invoicing has been mandatory for all Italian companies since January 2019.

To route an invoice, the SDI needs to know where to deliver it. There are several ways, and it is important not to confuse them:

ConceptField in B2BrouterWhat it is
Codice Destinatariorecipient_codeRouting code assigned by the SDI to the recipient. 7 characters for private companies, 6 for public administrations (Codice Ufficio).
PEC (Posta Elettronica Certificata)certified_emailCertified email, an alternative to the Codice Destinatario when the recipient does not have one.
Codice Fiscale / Partita IVAcin_value / tin_valueThe recipient’s tax identifier (not a routing code).
CUP / CIGfile_reference (CUP) / lot_reference (CIG)Project and tender codes required in certain operations with the public sector; they do not route the invoice.

To receive invoices in B2Brouter through the SDI, your supplier must use B2Brouter’s Codice Destinatario: EPKDZJB (after registering your company with the Agenzia delle Entrate).

The technical flow is the same in all three cases; what changes is the recipient’s identification:

  • B2B (company): identified by the Partita IVA (tin_scheme: 9906). The recipient_code is 7 characters (or 0000000 if the customer does not have one).
  • B2C (private consumer): identified by the Codice Fiscale (cin_scheme: 9907). They usually have no Codice Destinatario, so 0000000 is used and the SDI makes the invoice available in the citizen’s cassetto fiscale. On exempt invoices the bollo may apply.
  • B2G (public administration): identified by the Codice Ufficio, 6 characters. Mark the contact as public_sector: true and set the 6-character code in recipient_code. With the public sector, split payment often applies (see Esigibilità IVA and split payment).

B2Brouter validates the code length (7 private / 6 public sector) at send time.

  • An Italian company with a valid Partita IVA and a complete address (validated at send time, not when the account is created).
  • A B2Brouter test account: Register here.
  • API permissions (request them by opening a support ticket).

Start with the sandbox for your first payload tests. Move to staging (api-staging.b2brouter.net) when you want to test the full submission to the SDI test environment.

For an account to issue to the SDI, it must be enabled with a tax report setting for the sdi authority. See the Tax Report Settings Guide for the general details.

Terminal window
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/tax_report_settings \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'content-type: application/json' \
--data '{
"tax_report_setting": {
"code": "sdi"
}
}'

Response:

{
"tax_report_setting": {
"code": "sdi",
"start_date": null,
"auto_generate": true,
"auto_send": true,
"enabled": true,
"locked": false
}
}

On creation, B2Brouter enables enabled, auto_generate and auto_send by default, so FatturaPA invoices are generated and sent to the SDI automatically.

Terminal window
curl --request POST \
--url https://api-staging.b2brouter.net/accounts \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '{
"account": {
"country": "it",
"rounding_method": "half_up",
"tin_value": "20339260422",
"tin_scheme": 211,
"name": "Test Sender Italy",
"address": "Milano street, 1",
"city": "Milano",
"postalcode": "20019",
"province": "MI",
"email": "john.doe@example.com",
"tax_regime": "RF01"
}
}'

The tax_regime field states the issuer’s tax regime (see Regime fiscale); if omitted, RF01 (ordinary) is applied.

When you create an Italian customer:

  • Use tin_value for the Partita IVA with tin_scheme: 9906.
  • Use cin_value for the Codice Fiscale with cin_scheme: 9907 (scheme list). One of the two identifiers is enough.
  • The contact’s address and postal code are required to send to the SDI.
  • transport_type_code must be it.sdi.
  • document_type_code must be xml.fatturapa.1.2.
  • If the contact has a Codice Destinatario, set it in recipient_code (7 chars private / 6 public sector). If not, 0000000 is used. Optionally, set the buyer’s PEC in certified_email.
Terminal window
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/contacts \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--data '{
"contact": {
"tin_scheme": "9906",
"tin_value": "IT12345678901",
"name": "Esempio S.r.l.",
"address": "Via Inventata 88",
"postalcode": "00184",
"city": "Roma",
"province": "RM",
"country": "it",
"transport_type_code": "it.sdi",
"document_type_code": "xml.fatturapa.1.2",
"recipient_code": "{CODICE_DESTINATARIO}"
}
}'
Terminal window
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/invoices \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'content-type: application/json' \
--data '{
"send_after_import": true,
"invoice": {
"type": "IssuedInvoice",
"invoice_lines_attributes": [
{
"quantity": 10,
"price": 47,
"description": "Test Item",
"taxes_attributes": [
{ "name": "IVA", "percent": 22 }
]
}
],
"number": "1",
"date": "2025-01-22",
"due_date": "2025-02-22",
"contact_id": {YOUR_CONTACT_ID}
}
}'

Notes:

  • The invoice number can be at most 20 characters.
  • Each line must have a description and at least one tax.
  • due_date is optional for the it.sdi transport.
  • type_document (on the invoice): sets the FatturaPA TipoDocumento. Optional; if omitted, B2Brouter assigns it automatically. See Document type.
  • receipt_reference (on each line, not on the invoice): reference to a receipt/scontrino linked to the line, with the fields identifier, number and date. Optional, FatturaPA only:
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 47,
"description": "Test Item",
"taxes_attributes": [{ "name": "IVA", "percent": 22 }],
"receipt_reference": {
"identifier": "SCONTRINO-001",
"number": "42",
"date": "2025-01-20"
}
}
]

The TipoDocumento (type_document) classifies the invoice (fattura, nota di credito, autofattura…). B2Brouter assigns it automatically based on the invoice type and content, but you can force it with a valid code (TD01TD06, TD16TD27).

Behavior:

  • If you do not set type_document, it is assigned automatically: TD01 by default, TD04 for credit notes/amendments, TD16 when there are taxes with Natura N6, and TD17/TD18/TD19 for self-invoices (see Autofattura).
  • If you set it with a valid code, it is emitted as-is. If the value is not valid, it is ignored and the automatic assignment applies (no error is raised).

To see how each JSON field maps to the FatturaPA XML nodes, use the invoice field mapper.

When an operation carries no VAT (exempt, non-taxable, reverse charge…), FatturaPA does not use a percentage but a Natura code (N1N7). In B2Brouter it is set in category inside taxes_attributes, with percent: 0:

"taxes_attributes": [
{ "name": "IVA", "percent": 0, "category": "N2.1" }
]
CodeMeaning
N1Excluded pursuant to art. 15
N2.1 / N2.2Not subject (art. 7-bis and others)
N3.1N3.6Non-taxable (exports, intra-EU supplies, San Marino…)
N4Exempt
N5Margin scheme / VAT not shown
N6.1N6.9Reverse charge (inversione contabile) — see Autofattura
N7VAT chargeable in another EU state

B2Brouter fills in the reason associated with each Natura code automatically; you only need to set the category.

The issuer’s tax regime is set in the account’s tax_regime field. The default is RF01 (ordinary). Accepted values:

CodeRegimeCodeRegime
RF01OrdinarioRF12Agriturismo
RF02Contribuenti minimiRF13Vendite a domicilio
RF04Agricoltura e pescaRF14Rivendita beni usati / oggetti d’arte
RF05Vendita sali e tabacchiRF15Agenzie di vendite all’asta
RF06Commercio fiammiferiRF16IVA per cassa P.A.
RF07EditoriaRF17IVA per cassa
RF08Servizi telefonia pubblicaRF18Altro
RF09Documenti di trasporto/sostaRF19Regime forfettario
RF10Intrattenimenti e giochi
RF11Agenzie viaggi e turismo

The payment method is set with the payment_method field (on the contact or the invoice) and B2Brouter maps it to the FatturaPA ModalitaPagamento code (MPxx). See the Payment Method Guide for the payment_method values.

Most common mappings:

MP FatturaPAPayment method
MP01Cash (contanti)
MP02Cheque (assegno)
MP05Bank transfer (bonifico)
MP06Promissory note (vaglia cambiario)
MP08Credit/debit card

Esigibilità indicates when the VAT becomes due. It is set in the vat_payability field of the contact (default I):

ValueMeaning
IImmediate (default)
DDeferred (esigibilità differita)
SSplit payment (scissione dei pagamenti)

The value S (split payment) typically applies to invoices to the public sector: the buyer pays the VAT directly to the tax authority, so B2Brouter deducts it from the amount due (the subtotal and the VAT are shown, but the payable_amount does not include the VAT).

For professionals subject to withholding (ritenuta d’acconto), set amounts_withheld_reason (the payment reason) and the withheld amount on the invoice. B2Brouter deducts the amount from the amount due and generates the DatiRitenuta block in the XML.

There are two ways to state the amount:

  • amounts_withheld: the exact amount to withhold. Recommended when the withholding base is not the total (for example, only the taxable base).
  • withheld_percent: a percentage that B2Brouter applies to the invoice total (VAT included). Note: if you expect the withholding on the taxable base, use amounts_withheld.
{
"invoice": {
"type": "IssuedInvoice",
"withheld_percent": 20,
"amounts_withheld_reason": "A",
"...": "..."
}
}

With the example above on a base with VAT, the response returns the computed amounts_withheld and the payable_amount reduced by that amount.

The bollo (imposta di bollo) is a €2.00 stamp duty required on VAT-free invoices exceeding €77.47.

B2Brouter applies it automatically when all of these conditions are met:

  • The company has the bollo option enabled (apply_stamp_duty), which is configured on the company (not through this API).
  • The issuer is Italian and the total exceeds €77.47.
  • There is at least one tax with an applicable exemption Natura: N2.1, N2.2, N3.5, N3.6 or N4.
  • The document is not an integration/self-invoice (TD16TD19).

When it applies, the XML includes <DatiBollo> with BolloVirtuale: SI and ImportoBollo: 2.00. You can also add it manually as a line with the description “bollo”, price 2 and tax N1 at 0%.

To correct an invoice that has already been issued:

  • Nota di credito (credit note): create the invoice with is_credit_note: true. B2Brouter assigns TD04 automatically.
  • Nota di debito (debit note): set type_document: "TD05".

To link the original invoice (so the SDI relates the documents), add a reference with invoice_references of type amend, or set amended_invoice_number and amended_invoice_date.

Your suppliers’ invoices can reach B2Brouter through different reception channels:

  • SDI: register B2Brouter’s Codice Destinatario (EPKDZJB) with the Agenzia delle Entrate; Italian suppliers issue towards this code.
  • Peppol or email: enable the corresponding reception transport on the account to receive international suppliers that issue through those channels.

Whatever the channel, the invoice enters as a received invoice (ReceivedInvoice). To query them: GET /accounts/{id}/received_invoices (list) and GET /received_invoices/{id} (get).

When the supplier has not gone through the SDI (typically a foreign supplier), the purchase must be declared to the SDI as an autofattura. If you have a reception transport enabled, B2Brouter does it automatically when the invoice arrives; if you receive them in other software, you register them yourself (via the API) and they are integrated all the same. See the next section.

When you buy from a foreign supplier (or in internal reverse charge operations), Italian law requires you to self-invoice: declare the operation to the SDI on your own behalf.

In B2Brouter, the autofattura is not created as a separate document: it is generated automatically from the received invoice. The flow is:

  1. Make sure the account has the SDI enabled (with auto_generate, which is the default).
  2. Get the supplier’s invoice into the account as a received invoice: if you have a reception transport (Peppol/email) enabled, it arrives on its own; if you receive it in other software, register it via the API with POST /accounts/{id}/invoices and type: "ReceivedInvoice". Set the type_operation (services or goods) and the appropriate VAT Natura (e.g. N6.x for reverse charge); the supplier goes inside the contact object.
  3. On creation, B2Brouter automatically generates an integration tax report (the autofattura), exports it in FatturaPA format and, if the SDI has auto_send, sends it. You can query it with the tax reports endpoint.
Terminal window
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/invoices \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'content-type: application/json' \
--data '{
"invoice": {
"type": "ReceivedInvoice",
"number": "RIC-001",
"date": "2026-07-22",
"type_operation": "services",
"contact": {
"tin_scheme": "9930",
"tin_value": "DE137869841",
"name": "Fornitore Estero GmbH",
"address": "Berlin str 1",
"postalcode": "10115",
"city": "Berlin",
"country": "de"
},
"invoice_lines_attributes": [
{
"quantity": 1,
"price": 500,
"description": "Consulenza estera",
"taxes_attributes": [{ "name": "IVA", "percent": 0, "category": "N6.9" }]
}
]
}
}'

The integration’s TipoDocumento is computed automatically:

SituationCode
Internal reverse charge (taxes with Natura N6)TD16
Purchase of services from a non-resident supplierTD17
Intra-EU purchase of goodsTD18
Purchase of goods from an extra-EU supplierTD19

B2Brouter automatically fills in the DatiFattureCollegate block (reference to the received invoice) and marks the document as a self-invoice (SoggettoEmittente: CC).

The Fattura Elettronica Semplificata (art. 21-bis of DPR 633/72) is a reduced XML format for low-value transactions: the total cannot exceed €400. It is legally equivalent to an ordinary invoice, but uses its own structure (FatturaElettronicaSemplificata, version FSM10) with fewer required fields —for example, it does not export payment data—. Like any Italian invoice, it must be submitted to the SDI.

In B2Brouter it is created as an IssuedInvoice with the document type xml.fattura.semplificata.1.0.2 (on the invoice or the contact).

RequirementValue
Maximum invoice total€400
Buyer’s tax identifierCodice Fiscale or Partita IVA (one of the two required)
Buyer’s address and postal codeRequired
VAT per lineExactly one tax per line
Global discounts or chargesNot allowed (use line-level discounts)
Terminal window
curl --request POST \
--url https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/invoices \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'content-type: application/json' \
--data '{
"send_after_import": true,
"invoice": {
"type": "IssuedInvoice",
"document_type_code": "xml.fattura.semplificata.1.0.2",
"number": "S-2025-001",
"date": "2025-09-10",
"contact_id": 123456789,
"invoice_lines_attributes": [
{
"quantity": 2,
"price": 50.00,
"description": "Servizi di consulenza",
"taxes_attributes": [{ "name": "IVA", "percent": 22 }]
}
]
}
}'

For the simplified format, B2Brouter assigns the TipoDocumento automatically; do not set type_document:

  • TD07 — Fattura Semplificata (default).
  • TD08 — Nota di Credito Semplificata (when is_credit_note: true or an amendment).

If you set a valid code from the ordinary FatturaPA (TD01TD06, TD16TD27), it will be emitted as-is inside the simplified XML and the SDI will reject it (XSD error), because the simplified format only accepts TD07, TD08 and TD09. TD09 (Nota di Debito Semplificata) is not currently supported.

With send_after_import: true, the invoice is sent to the SDI through the it.sdi transport and goes through sendingsent. If the SDI (or the recipient) rejects it, it moves to error with the reason. When the SDI returns the registration identifier (IdentificativoSdI), it becomes available in to_net_id.

Terminal window
curl --request GET \
--url 'https://api-staging.b2brouter.net/invoices/{INVOICE_ID}?include=lines' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'
Terminal window
curl --request GET \
--url 'https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/invoices?offset=0&limit=25' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'

When the invoice reaches its final state, mark it as acknowledged to remove it from the active list:

Terminal window
curl --request POST \
--url https://api-staging.b2brouter.net/invoices/{INVOICE_ID}/ack \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'accept: application/json'

B2Brouter

Official documentation (Agenzia delle Entrate)