[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 typeWhat it tracksA0 (alta)A1 (modificación)B (baja / annullation)
ExpedidaIssued invoices
RecibidaReceived invoices
InversionCapital goods (bienes de inversión)
IntracomunitariaIntra-EU operations
MetalicoCash transactions ≥ €6,000 / counterparty / period
SeguroInsurance operations
ViajesagenciaTravel agency special regime
CobroPayments received against issued invoices
PagoPayments 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, or refused).
  • Polling: call GET /tax_reports/{id} repeatedly until state reaches 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ón
  • fecha_presentacion — the AEAT-assigned presentation timestamp
  • estado — the internal SII state. It starts at Nuevo, moves to Enviando while in flight, and settles on a terminal value: Correcto, Incorrecto, AceptadoConErrores, or Error.

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):

FieldMaps to AEAT elementNotes
simplified_art7273FacturaSimplificadaArticulos7.2_7.3Boolean. Emits S when true.
reg_previo_ggeeRegPrevioGGEEoREDEMEoCompetenciaBoolean.
macrodataMacrodatoBoolean (SII), unlike Verifactu's string version.
facturacion_disp_adicional_mercado_gasFacturacionDispAdicionalTerceraYsextayDelMercadoOrganizadoDelGasBoolean. Expedida only.
sin_identif_destinatario_art_6_1_dFacturaSinIdentifDestinatarioAritculo6.1.dBoolean. Expedida only.
succession_party_name + _tax_idEntidadSucedida > NombreRazon / NIFBoth required together when used.
billing_agreement_numberNumRegistroAcuerdoFacturacionMax 15 chars.
external_referenceRefExternaMax 60 chars.
tax_point_dateFechaOperacionOptional; 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:

FieldMaps to AEAT elementNotes
supplier_party_tax_id / _nameIDEmisorFactura > NIF / Contraparte > NombreRazonThe external supplier — the issuer of the invoice you received.
supplier_party_countryIDOtro > CodigoPaisWhen not es, the issuer is rendered via IDOtro instead of NIF.
tax_point_dateFechaOperacionOperation/service date. Omitted when not supplied.
fecha_contableFechaRegContableDate the invoice was recorded in your accounting books. If absent, falls back to tax_point_date then invoice_date.
cuota_deducible_in_centsCuotaDeducibleDeductible quota. When omitted, derived from sum(tax_breakdowns.tax_amount). Set explicitly to override (partial deductibility, etc.).
simplified_art7273FacturaSimplificadaArticulos7.2_7.3Boolean. Same shape as Expedida.
reg_previo_ggeeRegPrevioGGEEoREDEMEoCompetenciaBoolean.
macrodataMacrodatoBoolean.
succession_party_*EntidadSucedidaSame as Expedida.
billing_agreement_numberNumRegistroAcuerdoFacturacionSame as Expedida.
external_referenceRefExternaSame 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: SuministroLRCobrosMetalico envelope. Aggregate cash received from a counterparty ≥ €6,000 in a fiscal period.
  • Seguro: SuministroLROperacionesSeguros envelope. Adds a required ClaveOperacion mapped from operation_type.
  • Viajesagencia: SuministroLRAgenciasViajes envelope. 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 202 only means the batch was accepted for processing — not that any record was created. Poll status_url until status is "done", or consume the tax_report.state_change webhook 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, unsupported type) does not abort the batch — it appears as a per-record error in results.
  • Envelope errors (missing tax_reports key, not an array, more than 5000 records) return HTTP 400 synchronously, before anything is enqueued.
  • SII-only in v1: this endpoint accepts only type: "SiiDocument". A non-SII or missing type on any record is rejected synchronously with HTTP 422 (code: "unsupported_type") before anything is enqueued — that's an endpoint-contract error, distinct from per-record SII data errors (bad book_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 AEAT Cabecera, not by the per-record supplier_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_url until status is "done".

  • Webhook — subscribe an enabled webhook on your integration group to the sii_batch.finished event. 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_url for the full per-record results. The AEAT outcome of each created record still arrives via its own tax_report.state_change webhook.

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:

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)

B2Brouter FieldAEAT XML Node
fiscal_yearPeriodoLiquidacion > Ejercicio
fiscal_periodPeriodoLiquidacion > Periodo
supplier_party_tax_idIDFactura > IDEmisorFactura > NIF
invoice_numberIDFactura > NumSerieFacturaEmisor
invoice_dateIDFactura > FechaExpedicionFacturaEmisor
invoice_type_codeFacturaExpedida > TipoFactura
tax_point_dateFacturaExpedida > FechaOperacion
special_regime_keyFacturaExpedida > ClaveRegimenEspecialOTrascendencia
billing_agreement_numberFacturaExpedida > NumRegistroAcuerdoFacturacion
tax_inclusive_amountFacturaExpedida > ImporteTotal
descriptionFacturaExpedida > DescripcionOperacion
external_referenceFacturaExpedida > RefExterna
simplified_art7273FacturaExpedida > FacturaSimplificadaArticulos7.2_7.3
succession_party_nameFacturaExpedida > EntidadSucedida > NombreRazon
succession_party_tax_idFacturaExpedida > EntidadSucedida > NIF
reg_previo_ggeeFacturaExpedida > RegPrevioGGEEoREDEMEoCompetencia
macrodataFacturaExpedida > Macrodato
facturacion_disp_adicional_mercado_gasFacturaExpedida > FacturacionDispAdicionalTerceraYsextayDelMercadoOrganizadoDelGas
sin_identif_destinatario_art_6_1_dFacturaExpedida > FacturaSinIdentifDestinatarioAritculo6.1.d
customer_party_nameFacturaExpedida > Contraparte > NombreRazon
customer_party_tax_idFacturaExpedida > Contraparte > NIF
tax_breakdowns[*].percentDetalleIVA > TipoImpositivo
tax_breakdowns[*].taxable_baseDetalleIVA > BaseImponible
tax_breakdowns[*].tax_amountDetalleIVA > CuotaRepercutida (omitted when zero)

Recibida (SuministroLRFacturasRecibidas)

B2Brouter FieldAEAT XML Node
fiscal_yearPeriodoLiquidacion > Ejercicio
fiscal_periodPeriodoLiquidacion > Periodo
supplier_party_tax_idIDFactura > IDEmisorFactura > NIF (or IDOtro > ID for non-ES)
supplier_party_countryIDFactura > IDEmisorFactura > IDOtro > CodigoPais (non-ES only)
supplier_party_nameFacturaRecibida > Contraparte > NombreRazon
invoice_numberIDFactura > NumSerieFacturaEmisor
invoice_dateIDFactura > FechaExpedicionFacturaEmisor
invoice_type_codeFacturaRecibida > TipoFactura
tax_point_dateFacturaRecibida > FechaOperacion
special_regime_keyFacturaRecibida > ClaveRegimenEspecialOTrascendencia
billing_agreement_numberFacturaRecibida > NumRegistroAcuerdoFacturacion
tax_inclusive_amountFacturaRecibida > ImporteTotal
descriptionFacturaRecibida > DescripcionOperacion
external_referenceFacturaRecibida > RefExterna
simplified_art7273FacturaRecibida > FacturaSimplificadaArticulos7.2_7.3
succession_party_nameFacturaRecibida > EntidadSucedida > NombreRazon
succession_party_tax_idFacturaRecibida > EntidadSucedida > NIF
reg_previo_ggeeFacturaRecibida > RegPrevioGGEEoREDEMEoCompetencia
macrodataFacturaRecibida > Macrodato
tax_breakdowns[*].percentDetalleIVA > TipoImpositivo
tax_breakdowns[*].taxable_baseDetalleIVA > BaseImponible
tax_breakdowns[*].tax_amountDetalleIVA > CuotaSoportada (omitted when zero)
fecha_contableFacturaRecibida > FechaRegContable
cuota_deducible_in_centsFacturaRecibida > CuotaDeducible

Tax breakdown category → desglose bucket

Each 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):

categoryAEAT 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 (CausaExencionexemption_code)
NS, O, G, N3.1, N3.3N3.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-estate DatosInmueble deferral.

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

The 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

For 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/2012
  • F3 — Factura emitida en sustitución de facturas simplificadas facturadas y declaradas
  • F4 — Asiento resumen de facturas
  • F5 — Importaciones (DUA)
  • F6 — Otros justificantes contables
  • R1 — 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)

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)

  • D — Declarante
  • R — 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.