[Beta] SII Guide
[Beta] Report VAT records to the AEAT via SII (Suministro Inmediato de Información) using the B2Brouter API
[Beta] — staging only. The SII JSON Tax Report API described in this guide is currently available on the staging environment (
https://api-staging.b2brouter.net) for integration testing. It is not yet enabled in production. Field names, endpoints and behaviour may still change before the production release.
The Spanish Suministro Inmediato de Información (SII) system requires registered businesses to keep their VAT books — issued invoices, received invoices, capital goods, intra-EU operations, cash transactions over €6,000, and others — submitted in near-real-time to the AEAT ("Agencia Estatal de Administración Tributaria"). SII is mandatory for Grandes Empresas (turnover > €6,010,121.04), VAT-group members, REDEME participants, and other businesses that have opted in.
B2Brouter offers a JSON Tax Report API for SII so your system doesn't have to deal with SOAP, AEAT certificates, batched submissions, response parsing, or the per-book XML envelopes. You issue REST calls; B2Brouter handles the rest, batching records into AEAT "Libros de Registro" and submitting them under a Social Collaborator certificate.
What is SII?
SII is a tax-reporting system distinct from invoicing — there is no QR code on the underlying invoice and no consumer-facing verification step. Each record represents one book entry (a single invoice you issued, a single invoice you received, one capital good's annual prorrata, etc.). The AEAT collects records over the year and uses them for VAT cross-checking.
SII has nine book types ("libros"), each with its own AEAT XML envelope and validation rules:
| Book type | What it tracks | A0 (alta) | A1 (modificación) | B (baja / annullation) |
|---|---|---|---|---|
| Expedida | Issued invoices | ✓ | ✓ | ✓ |
| Recibida | Received invoices | ✓ | ✓ | ✓ |
| Inversion | Capital goods (bienes de inversión) | ✓ | ✓ | ✓ |
| Intracomunitaria | Intra-EU operations | ✓ | ✓ | ✓ |
| Metalico | Cash transactions ≥ €6,000 / counterparty / period | ✓ | ✓ | ✓ |
| Seguro | Insurance operations | ✓ | ✓ | ✓ |
| Viajesagencia | Travel agency special regime | ✓ | ✓ | ✓ |
| Cobro | Payments received against issued invoices | ✓ | — | — |
| Pago | Payments made against received invoices | ✓ | — | — |
Cobro and Pago return HTTP 422 on PATCH and DELETE — the AEAT XSD provides no A1 or B variants for those books.
Expedida and Recibida cover 98%+ of the volume in production. The remaining seven books support the long tail of VAT-reporting requirements.
Setting up and working with the B2Brouter API
The first step is to enable the SII tax authority for the company that will be reporting. You can refer to the Tax Report Settings Guide for a full description. The minimal call:
Example request:
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": "sii",
"start_date": "2026-04-15",
"auto_send": true
}
}'Once a setting exists for code: "sii" on the account, all SII Tax Report API calls under that account use it.
Tax Report API
The Tax Report API is one endpoint family that handles all nine SII books. The book is selected by the book_type field on the request body. The type field is SiiDocument for every SII record regardless of book.
Create a Tax Report
POST /accounts/{ACCOUNT_ID}/tax_reports creates one SII record. The same endpoint is used by Verifactu, TicketBai, and KSeF; the type field switches which validation set + which AEAT XML envelope applies.
The structure of the JSON payload is based on PEPPOL Continuous Transaction Control (CTC) — the CTC field names do not match the AEAT XML node names. The Equivalence between B2Brouter internal tax report fields and SII XML nodes section below lists the mapping for each book.
Tax reports have tax breakdowns ("desglose" in Spanish), not invoice lines. Your system aggregates the underlying invoice lines by tax type/category and submits the breakdown sums. B2Brouter performs validation but does not compute breakdowns from line data.
Lifecycle
After a successful POST /tax_reports, the response carries a state: "processing" and a ledger_id. SII records are batched into "Libros de Registro" (Ledgers) on the server side and submitted to the AEAT asynchronously. To track the lifecycle:
- Recommended: configure the tax report webhook. B2Brouter calls your endpoint when the record reaches a final state (
registered,error,registered_with_errors,annulled, orrefused). - Polling: call
GET /tax_reports/{id}repeatedly untilstatereaches one of the final values.
The AEAT response is reflected in three SII-specific fields on the record once received:
csv— the AEAT-assigned Código Seguro de Verificaciónfecha_presentacion— the AEAT-assigned presentation timestampestado— the internal SII state. It starts atNuevo, moves toEnviandowhile in flight, and settles on a terminal value:Correcto,Incorrecto,AceptadoConErrores, orError.
For error states the record's errors array is populated with the AEAT rejection, each entry carrying a code and a description:
{
"errors": [
{ "code": "4102", "description": "El NIF no está identificado en el censo de la AEAT" }
]
}Get the XML representation
GET /tax_reports/{id}/download returns the per-record XML that B2Brouter will (or did) include in the AEAT submission. The XML is generated at create time and persisted, so it's available immediately after POST. The bundle that actually goes on the wire is the concatenated Ledger XML — see Ledgers API.
Correct a Tax Report (A1)
PATCH /tax_reports/{id} with the corrected fields creates a sibling record with operacion: A1 (modificación). The original record stays in place; the new sibling is linked via corrected_by_id on the original and is what gets re-submitted to the AEAT.
The book a record belongs to is fixed at create time — book_type cannot be changed on an A1 sibling. Cobro and Pago return 422; their AEAT XSD does not define an A1 envelope.
Cancel a Tax Report (B)
DELETE /tax_reports/{id} creates a sibling record with operacion: B (baja / annullation). The original is linked via annulled_by_id. Cobro and Pago return 422 — their AEAT XSD does not define a baja envelope.
List Tax Reports
GET /accounts/{ACCOUNT_ID}/tax_reports returns the account's tax reports — SII, Verifactu, TicketBai, KSeF, all in one list. Filter by date range or other criteria as documented in the list tax reports endpoint.
Expedida (issued invoices)
The most common SII book. The reporting company is the invoice issuer; the customer is the counterparty.
Example request
curl --request POST \
--url 'https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/tax_reports' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Content-Type: application/json' \
--data '{
"tax_report": {
"type": "SiiDocument",
"book_type": "Expedida",
"invoice_number": "INV-2026-001",
"invoice_date": "2026-04-15",
"fiscal_year": 2026,
"fiscal_period": 4,
"invoice_type_code": "F1",
"special_regime_key": "01",
"description": "Servicios de consultoría",
"customer_party_name": "Cliente Ejemplo S.L.",
"customer_party_tax_id": "B87654321",
"customer_party_country": "es",
"tax_inclusive_amount": 1210.0,
"tax_amount": 210.0,
"currency": "EUR",
"tax_breakdowns": [
{
"name": "IVA",
"category": "S",
"percent": 21.0,
"taxable_base": 1000.0,
"tax_amount": 210.0
}
]
}
}'Expedida-specific fields (in addition to the common CTC fields):
| Field | Maps to AEAT element | Notes |
|---|---|---|
simplified_art7273 | FacturaSimplificadaArticulos7.2_7.3 | Boolean. Emits S when true. |
reg_previo_ggee | RegPrevioGGEEoREDEMEoCompetencia | Boolean. |
macrodata | Macrodato | Boolean (SII), unlike Verifactu's string version. |
facturacion_disp_adicional_mercado_gas | FacturacionDispAdicionalTerceraYsextayDelMercadoOrganizadoDelGas | Boolean. Expedida only. |
sin_identif_destinatario_art_6_1_d | FacturaSinIdentifDestinatarioAritculo6.1.d | Boolean. Expedida only. |
succession_party_name + _tax_id | EntidadSucedida > NombreRazon / NIF | Both required together when used. |
billing_agreement_number | NumRegistroAcuerdoFacturacion | Max 15 chars. |
external_reference | RefExterna | Max 60 chars. |
tax_point_date | FechaOperacion | Optional; defaults to omitted. |
The AEAT XML envelope is SuministroLRFacturasEmitidas. The five S/N flags above are optional and emit only when truthy.
Recibida (received invoices)
Recibida has the issuer/receiver flip compared to Expedida: the external supplier is the issuer (recorded in IDEmisorFactura), and the reporting company is the receiver (Titular). Use supplier_party_* for the external supplier; customer_party_* is not required (the company defaults are used as the receiver).
Example request
curl --request POST \
--url 'https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/tax_reports' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Content-Type: application/json' \
--data '{
"tax_report": {
"type": "SiiDocument",
"book_type": "Recibida",
"invoice_number": "PROV-555",
"invoice_date": "2026-04-15",
"tax_point_date": "2026-04-10",
"fecha_contable": "2026-04-22",
"fiscal_year": 2026,
"fiscal_period": 4,
"invoice_type_code": "F1",
"special_regime_key": "01",
"description": "Compra de material",
"supplier_party_name": "Proveedor Test S.L.",
"supplier_party_tax_id": "A12345674",
"supplier_party_country": "es",
"tax_inclusive_amount": 1210.0,
"tax_amount": 210.0,
"cuota_deducible_in_cents": 21000,
"currency": "EUR",
"tax_breakdowns": [
{
"name": "IVA",
"category": "S",
"percent": 21.0,
"taxable_base": 1000.0,
"tax_amount": 210.0
}
]
}
}'Recibida-specific fields:
| Field | Maps to AEAT element | Notes |
|---|---|---|
supplier_party_tax_id / _name | IDEmisorFactura > NIF / Contraparte > NombreRazon | The external supplier — the issuer of the invoice you received. |
supplier_party_country | IDOtro > CodigoPais | When not es, the issuer is rendered via IDOtro instead of NIF. |
tax_point_date | FechaOperacion | Operation/service date. Omitted when not supplied. |
fecha_contable | FechaRegContable | Date the invoice was recorded in your accounting books. If absent, falls back to tax_point_date then invoice_date. |
cuota_deducible_in_cents | CuotaDeducible | Deductible quota. When omitted, derived from sum(tax_breakdowns.tax_amount). Set explicitly to override (partial deductibility, etc.). |
simplified_art7273 | FacturaSimplificadaArticulos7.2_7.3 | Boolean. Same shape as Expedida. |
reg_previo_ggee | RegPrevioGGEEoREDEMEoCompetencia | Boolean. |
macrodata | Macrodato | Boolean. |
succession_party_* | EntidadSucedida | Same as Expedida. |
billing_agreement_number | NumRegistroAcuerdoFacturacion | Same as Expedida. |
external_reference | RefExterna | Same as Expedida. |
The two Expedida-only flags (facturacion_disp_adicional_mercado_gas, sin_identif_destinatario_art_6_1_d) do not appear on Recibida — the AEAT XSD defines them only on FacturaExpedidaType.
The AEAT XML envelope is SuministroLRFacturasRecibidas.
Non-ES suppliers
When supplier_party_country is not es, the issuer is emitted as IDOtro instead of NIF:
<sii:IDEmisorFactura>
<sii:IDOtro>
<sii:CodigoPais>FR</sii:CodigoPais>
<sii:IDType>04</sii:IDType>
<sii:ID>FR12345678901</sii:ID>
</sii:IDOtro>
</sii:IDEmisorFactura>The Contraparte block is also rendered with IDOtro to match.
Other books (Inversion, Intracomunitaria, Metalico, Seguro, Viajesagencia, Cobro, Pago)
The remaining seven books each cover a specific reporting requirement. They share the common CTC fields and the same lifecycle, but carry book-specific scalars. Refer to the OpenAPI reference for the full field list.
Inversion (capital goods)
Tracks the annual prorrata + regularization of capital goods. AEAT envelope: SuministroLRBienesInversion. Required fields (in addition to the common CTC envelope):
identificacion_bien— asset identifier (IdentificacionBien, max 40 chars)fecha_inicio_utilizacion— date the asset entered use (FechaInicioUtilizacion)prorrata_anual_definitiva— definitive annual prorrata (ProrrataAnualDefinitiva, 0–100, 2 decimals)
Optional: regularizacion_anual_deduccion_in_cents, identificacion_entrega, regularizacion_deduccion_efectuada_in_cents, external_reference, billing_agreement_number, succession_party_*.
Intracomunitaria (intra-EU operations)
AEAT envelope: SuministroLRDetOperacionIntracomunitaria. Required: clave_declarado (enum D or R), external_reference (per AEAT XSD), plus description mapped to DescripcionBienes. Optional: plazo_operacion, facturas_o_documentacion.
Metalico / Seguro / Viajesagencia (period-aggregated)
These books are period-aggregated: a single record represents the total from one counterparty in one fiscal period. There is no invoice_number or invoice_date — the AEAT XSD omits the IDFactura block. The counterparty is supplied via customer_party_* (B2Brouter mirrors it onto nombre_contraparte server-side).
- Metalico:
SuministroLRCobrosMetalicoenvelope. Aggregate cash received from a counterparty ≥ €6,000 in a fiscal period. - Seguro:
SuministroLROperacionesSegurosenvelope. Adds a requiredClaveOperacionmapped fromoperation_type. - Viajesagencia:
SuministroLRAgenciasViajesenvelope. Structurally identical to Metalico per the AEAT XSD.
Cobro / Pago (payment tracking)
Cobro: payments received against issued invoices. Pago: payments made against received invoices. Both carry a per-record array of payment events in payload_data:
{
"payload_data": {
"cobros": [
{ "fecha": "2026-03-01", "importe_in_cents": 50000, "medio": "01", "cuenta_o_medio": "ES1234..." },
{ "fecha": "2026-04-01", "importe_in_cents": 50000, "medio": "01", "cuenta_o_medio": "ES1234..." }
]
}
}(Use pagos instead of cobros for Pago.)
No A1 or B variants exist for these books per the AEAT XSD — PATCH and DELETE return 422.
Bulk endpoint (asynchronous)
For high-volume integrations (point-of-sale aggregations, CSV-to-JSON migrations, etc.), use POST /accounts/{ACCOUNT_ID}/tax_report_batches to submit up to 5000 records in a single request. Processing is asynchronous: the call validates only the envelope and returns 202 Accepted with a batch id; the per-record build, validation and ledger attach run in a background job. You then poll the batch-status endpoint (or rely on the per-record tax_report.state_change webhook once the AEAT responds).
curl --request POST \
--url 'https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/tax_report_batches' \
--header 'X-B2B-API-Key: {YOUR_API_KEY}' \
--header 'X-B2B-API-Version: {YOUR_API_VERSION}' \
--header 'Content-Type: application/json' \
--data '{
"tax_reports": [
{ "type": "SiiDocument", "book_type": "Expedida", "invoice_number": "INV-001", ... },
{ "type": "SiiDocument", "book_type": "Expedida", "invoice_number": "INV-002", ... },
...
]
}'Accepted response (202):
{
"batch_id": 4567,
"status": "processing",
"total": 2,
"status_url": "https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/tax_report_batches/4567"
}Poll the batch status at GET /accounts/{ACCOUNT_ID}/tax_report_batches/{BATCH_ID}. While the job runs status is "processing"; once finished it is "done" and results is populated, in submission order:
{
"batch_id": 4567,
"status": "done",
"summary": { "total": 3, "accepted": 2, "rejected": 1 },
"results": [
{ "index": 0, "status": "created", "tax_report": { "id": 12345, ... } },
{ "index": 1, "status": "error", "errors": [{ "code": "invalid_book_type", ... }] },
{ "index": 2, "status": "created", "tax_report": { "id": 12346, ... } }
]
}Semantics:
- Asynchronous: the
202only means the batch was accepted for processing — not that any record was created. Pollstatus_urluntilstatusis"done", or consume thetax_report.state_changewebhook for the AEAT outcome of each created record. - Partial success: each record is validated independently in the job. A failure on one record (bad
book_type, XSD-invalid XML, unsupportedtype) does not abort the batch — it appears as a per-recorderrorinresults. - Envelope errors (missing
tax_reportskey, not an array, more than 5000 records) return HTTP400synchronously, before anything is enqueued. - SII-only in v1: this endpoint accepts only
type: "SiiDocument". A non-SII or missingtypeon any record is rejected synchronously with HTTP422(code: "unsupported_type") before anything is enqueued — that's an endpoint-contract error, distinct from per-record SII data errors (badbook_type, XSD-invalid), which are reported as partial failures in the batch status. - Ledger attach + send: created records are attached to mode-segregated SII ledgers grouped by
(document_type_code, reporting-company TIN)— i.e. by the Titular declared in the AEATCabecera, not by the per-recordsupplier_party_tax_id. For Recibida/Pago that field holds the external supplier, so records from different suppliers still share one ledger as long as they belong to the same book and reporting company. Ledgers are then submitted to the AEAT by the per-minute cron (see Ledgers API) — never on the request thread.
Knowing when the batch has finished
Two options, use either or both:
-
Poll
status_urluntilstatusis"done". -
Webhook — subscribe an enabled webhook on your integration group to the
sii_batch.finishedevent. When the ingest job finishes, B2Brouter POSTs:{ "code": "sii_batch.finished", "triggered_at": 1748000000, "data": { "batch_id": 4567, "status": "done", "summary": { "total": 3, "accepted": 2, "rejected": 1 }, "status_url": "https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/tax_report_batches/4567" } }The webhook body carries only the summary (bounded regardless of batch size);
GET status_urlfor the full per-record results. The AEAT outcome of each created record still arrives via its owntax_report.state_changewebhook.
Ledgers API
SII tax reports are not sent individually to the AEAT — they are bundled into "Libros de Registro" (Ledger in B2Brouter terms), holding up to 5000 records per book / reporting company (Titular). Each tax report has a ledger_id field pointing to the internal Ledger ID. With that ID you can:
- Retrieve the XML representation of the Ledger via the download ledger endpoint. This is the document B2Brouter sends to the AEAT.
- Retrieve the AEAT XML response to that Ledger via the download response endpoint.
Modern SII Ledgers are flushed to the AEAT exclusively by the per-minute cron driven by the Strategy::Sii policy (60 seconds between submissions, or immediately once a Ledger reaches 5000 records). Neither POST /tax_reports, PATCH/DELETE, nor the bulk endpoint ever submit to the AEAT on the request thread — they attach the record(s) and return; the cron does the send. Expect up to ~60s between create and submission.
Equivalence between B2Brouter internal tax report fields and SII XML nodes
Expedida (SuministroLRFacturasEmitidas)
SuministroLRFacturasEmitidas)| B2Brouter Field | AEAT XML Node |
|---|---|
fiscal_year | PeriodoLiquidacion > Ejercicio |
fiscal_period | PeriodoLiquidacion > Periodo |
supplier_party_tax_id | IDFactura > IDEmisorFactura > NIF |
invoice_number | IDFactura > NumSerieFacturaEmisor |
invoice_date | IDFactura > FechaExpedicionFacturaEmisor |
invoice_type_code | FacturaExpedida > TipoFactura |
tax_point_date | FacturaExpedida > FechaOperacion |
special_regime_key | FacturaExpedida > ClaveRegimenEspecialOTrascendencia |
billing_agreement_number | FacturaExpedida > NumRegistroAcuerdoFacturacion |
tax_inclusive_amount | FacturaExpedida > ImporteTotal |
description | FacturaExpedida > DescripcionOperacion |
external_reference | FacturaExpedida > RefExterna |
simplified_art7273 | FacturaExpedida > FacturaSimplificadaArticulos7.2_7.3 |
succession_party_name | FacturaExpedida > EntidadSucedida > NombreRazon |
succession_party_tax_id | FacturaExpedida > EntidadSucedida > NIF |
reg_previo_ggee | FacturaExpedida > RegPrevioGGEEoREDEMEoCompetencia |
macrodata | FacturaExpedida > Macrodato |
facturacion_disp_adicional_mercado_gas | FacturaExpedida > FacturacionDispAdicionalTerceraYsextayDelMercadoOrganizadoDelGas |
sin_identif_destinatario_art_6_1_d | FacturaExpedida > FacturaSinIdentifDestinatarioAritculo6.1.d |
customer_party_name | FacturaExpedida > Contraparte > NombreRazon |
customer_party_tax_id | FacturaExpedida > Contraparte > NIF |
tax_breakdowns[*].percent | DetalleIVA > TipoImpositivo |
tax_breakdowns[*].taxable_base | DetalleIVA > BaseImponible |
tax_breakdowns[*].tax_amount | DetalleIVA > CuotaRepercutida (omitted when zero) |
Recibida (SuministroLRFacturasRecibidas)
SuministroLRFacturasRecibidas)| B2Brouter Field | AEAT XML Node |
|---|---|
fiscal_year | PeriodoLiquidacion > Ejercicio |
fiscal_period | PeriodoLiquidacion > Periodo |
supplier_party_tax_id | IDFactura > IDEmisorFactura > NIF (or IDOtro > ID for non-ES) |
supplier_party_country | IDFactura > IDEmisorFactura > IDOtro > CodigoPais (non-ES only) |
supplier_party_name | FacturaRecibida > Contraparte > NombreRazon |
invoice_number | IDFactura > NumSerieFacturaEmisor |
invoice_date | IDFactura > FechaExpedicionFacturaEmisor |
invoice_type_code | FacturaRecibida > TipoFactura |
tax_point_date | FacturaRecibida > FechaOperacion |
special_regime_key | FacturaRecibida > ClaveRegimenEspecialOTrascendencia |
billing_agreement_number | FacturaRecibida > NumRegistroAcuerdoFacturacion |
tax_inclusive_amount | FacturaRecibida > ImporteTotal |
description | FacturaRecibida > DescripcionOperacion |
external_reference | FacturaRecibida > RefExterna |
simplified_art7273 | FacturaRecibida > FacturaSimplificadaArticulos7.2_7.3 |
succession_party_name | FacturaRecibida > EntidadSucedida > NombreRazon |
succession_party_tax_id | FacturaRecibida > EntidadSucedida > NIF |
reg_previo_ggee | FacturaRecibida > RegPrevioGGEEoREDEMEoCompetencia |
macrodata | FacturaRecibida > Macrodato |
tax_breakdowns[*].percent | DetalleIVA > TipoImpositivo |
tax_breakdowns[*].taxable_base | DetalleIVA > BaseImponible |
tax_breakdowns[*].tax_amount | DetalleIVA > CuotaSoportada (omitted when zero) |
fecha_contable | FacturaRecibida > FechaRegContable |
cuota_deducible_in_cents | FacturaRecibida > CuotaDeducible |
Tax breakdown category → desglose bucket
category → desglose bucketEach tax_breakdowns[*].category (the standard PEPPOL/UN-CEFACT VAT category code)
decides which AEAT desglose bucket the line lands in. This is the canonical
signal — send the right category; the boolean flags are derived from it.
Expedida (TipoDesglose > DesgloseFactura):
category | AEAT bucket |
|---|---|
S, Z, H, AA, AAA, N1 (taxable) | Sujeta > NoExenta, TipoNoExenta = S1 |
AE (reverse charge) | Sujeta > NoExenta, TipoNoExenta = S2 |
E, K, N2.*, N3.2, N3.6, N4, N5, N7, IC (exempt) | Sujeta > Exenta > DetalleExenta (CausaExencion ← exemption_code) |
NS, O, G, N3.1, N3.3–N3.5 (not subject) | NoSujeta (ImporteTAIReglasLocalizacion when no_subject_code = RL, else ImportePorArticulos7_14_Otros) |
A mix of S1 and S2 lines collapses to a single TipoNoExenta = S3. A line
with no recognised category renders as S1 (fully taxable).
Recibida (DesgloseFactura): reverse-charge lines (category AE) go in
InversionSujetoPasivo; everything else in DesgloseIVA.
Not yet supported:
DesgloseTipoOperacion(the goods-vs-services split AEAT requires for some cross-border counterparties). Invoices that need it should use the legacy CSV path until a later release adds support — mirrors the real-estateDatosInmuebledeferral.
XSD files
The AEAT publishes the canonical SII XSDs. B2Brouter validates rendered XML against SuministroInformacion.xsd and SuministroLR.xsd (v1.1) on every send. The schemas live in this repo at vendor/xsd-validator/lib/xsd/schemas/sii_v11/.
Code descriptions for specific fields
book_type
book_typeThe values are listed in What is SII? above. The book_type drives the AEAT XML envelope, the required-field set, and the validation rules.
invoice_type_code
invoice_type_codeFor Expedida and Recibida:
F1— Factura (art. 6, 7.2 y 7.3 del RD 1619/2012)F2— Factura Simplificada y Facturas sin identificación del destinatario (art. 6.1.d) RD 1619/2012F3— Factura emitida en sustitución de facturas simplificadas facturadas y declaradasF4— Asiento resumen de facturasF5— Importaciones (DUA)F6— Otros justificantes contablesR1— Factura Rectificativa (Error fundado en derecho y Art. 80 Uno Dos y Seis LIVA)R2— Factura Rectificativa (Art. 80.3)R3— Factura Rectificativa (Art. 80.4)R4— Factura Rectificativa (Resto)R5— Factura Rectificativa en facturas simplificadas
special_regime_key (ClaveRegimenEspecialOTrascendencia)
special_regime_key (ClaveRegimenEspecialOTrascendencia)A two-digit AEAT code describing the special regime applied to the operation. The most common values are 01 (régimen general), 02 (exportaciones), 03 (régimen especial bienes usados), 04 (régimen especial oro de inversión), 05 (régimen especial agencias de viajes), 06 (régimen especial grupo de entidades), 07 (régimen especial criterio de caja), 08 (operaciones IPSI / IGIC). See the AEAT specifications for the full list and the per-book applicability.
clave_declarado (Intracomunitaria only)
clave_declarado (Intracomunitaria only)D— DeclaranteR— Receptor
Error checking
B2Brouter validates SII payloads against the AEAT XSDs before any wire submission. A validation failure returns HTTP 422 with the failing fields itemized in JSON. If the AEAT itself rejects a submission (e.g. unknown counterparty NIF), the rejection is reflected in the record's state (typically error or registered_with_errors) and the AEAT's error_code / error_description fields. Configure the tax report webhook to react to final states without polling.
Updated about 4 hours ago