Ir al contenido
Log in

[Beta] Informa registros de IVA a la AEAT mediante SII (Suministro Inmediato de Información) con la API de B2Brouter

[Beta] — solo en staging. La API JSON Tax Report para SII descrita en esta guía está disponible actualmente en el entorno de staging (https://api-staging.b2brouter.net) para pruebas de integración. Todavía no está habilitada en producción. Los nombres de campos, endpoints y comportamiento pueden cambiar antes del lanzamiento en producción.

El sistema español Suministro Inmediato de Información (SII) obliga a las empresas registradas a mantener sus libros de IVA — facturas emitidas, facturas recibidas, bienes de inversión, operaciones intracomunitarias, operaciones en metálico superiores a 6.000 €, entre otros — enviados en tiempo casi real a la AEAT (“Agencia Estatal de Administración Tributaria”). El SII es obligatorio para las Grandes Empresas (facturación > 6.010.121,04 €), los miembros de grupos de IVA, los participantes en el REDEME y otras empresas que se hayan acogido voluntariamente.

B2Brouter ofrece una API JSON Tax Report para SII para que tu sistema no tenga que gestionar SOAP, certificados de la AEAT, envíos por lotes, análisis de respuestas ni los wrappers XML por libro. Tú realizas llamadas REST; B2Brouter se encarga del resto, agrupando los registros en “Libros de Registro” de la AEAT y enviándolos bajo un certificado de Colaborador Social.

El SII es un sistema de información tributaria distinto de la facturación — la factura subyacente no lleva ningún código QR ni hay ningún paso de verificación de cara al consumidor. Cada registro representa una entrada en un libro (una factura emitida, una factura recibida, la prorrata anual de un bien de inversión, etc.). La AEAT recopila los registros a lo largo del año y los utiliza para la comprobación cruzada del IVA.

El SII tiene nueve tipos de libros (“libros”), cada uno con su propio wrapper XML de la AEAT y sus reglas de validación:

Tipo de libroQué registraA0 (alta)A1 (modificación)B (baja / anulación)
ExpedidaFacturas emitidas
RecibidaFacturas recibidas
InversionBienes de inversión
IntracomunitariaOperaciones intracomunitarias
MetalicoOperaciones en metálico ≥ 6.000 € / contrapartida / período
SeguroOperaciones de seguros
ViajesagenciaRégimen especial de agencias de viajes
CobroCobros recibidos de facturas emitidas
PagoPagos realizados de facturas recibidas

Cobro y Pago devuelven HTTP 422 en PATCH y DELETE — el XSD de la AEAT no contempla variantes A1 ni B para esos libros.

Expedida y Recibida concentran el 98%+ del volumen en producción. Los siete libros restantes cubren la larga cola de requisitos de información de IVA.

Configuración y trabajo con la API de B2Brouter

Sección titulada «Configuración y trabajo con la API de B2Brouter»

El primer paso es habilitar la autoridad tributaria SII para la empresa que va a informar. Puedes consultar la Tax Report Settings Guide para una descripción completa. La llamada mínima:

Ejemplo de petición:

Ventana de terminal
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
}
}'

Una vez que existe una configuración con code: "sii" en la cuenta, todas las llamadas a la API Tax Report SII de esa cuenta la utilizan.

La API Tax Report es una familia de endpoints que gestiona los nueve libros SII. El libro se selecciona con el campo book_type del cuerpo de la petición. El campo type es SiiDocument para todos los registros SII independientemente del libro.

POST /accounts/{ACCOUNT_ID}/tax_reports crea un registro SII. El mismo endpoint lo utilizan Verifactu, TicketBai y KSeF; el campo type determina qué conjunto de validación + qué wrapper XML de la AEAT se aplica.

La estructura del payload JSON se basa en el PEPPOL Continuous Transaction Control (CTC) — los nombres de campos CTC no coinciden con los nombres de nodos del XML de la AEAT. La sección Equivalencia entre los campos internos de tax report de B2Brouter y los nodos XML del SII más abajo recoge el mapeo para cada libro.

Los tax reports tienen desgloses fiscales (“desglose”), no líneas de factura. Tu sistema agrega las líneas de factura subyacentes por tipo/categoría fiscal y envía las sumas del desglose. B2Brouter realiza la validación pero no calcula los desgloses a partir de los datos de las líneas.

Tras un POST /tax_reports satisfactorio, la respuesta lleva un state: "processing" y un ledger_id. Los registros SII se agrupan en “Libros de Registro” (Ledgers) en el servidor y se envían a la AEAT de manera asíncrona. Para hacer el seguimiento del ciclo de vida:

  • Recomendado: configura el webhook de tax report. B2Brouter llama a tu endpoint cuando el registro llega a un estado final (registered, error, registered_with_errors, annulled o refused).
  • Polling: llama a GET /tax_reports/{id} repetidamente hasta que state llegue a uno de los valores finales.

La respuesta de la AEAT se refleja en tres campos específicos de SII en el registro una vez recibida:

  • csv — el Código Seguro de Verificación asignado por la AEAT
  • fecha_presentacion — el timestamp de presentación asignado por la AEAT
  • estado — el estado interno del SII. Comienza en Nuevo, pasa a Enviando mientras está en tránsito y se estabiliza en un valor terminal: Correcto, Incorrecto, AceptadoConErrores o Error.

Para los estados de error, el array errors del registro se rellena con el rechazo de la AEAT, donde cada entrada lleva un code y una description:

{
"errors": [
{
"code": "4102",
"description": "El NIF no está identificado en el censo de la AEAT"
}
]
}

GET /tax_reports/{id}/download devuelve el XML por registro que B2Brouter incluirá (o incluyó) en el envío a la AEAT. El XML se genera en el momento de la creación y se persiste, de modo que está disponible inmediatamente después del POST. El paquete que se envía realmente es el XML del Ledger concatenado — consulta la API de Ledgers.

PATCH /tax_reports/{id} con los campos corregidos crea un registro hermano con operacion: A1 (modificación). El registro original se mantiene; el nuevo hermano se enlaza mediante corrected_by_id en el original y es el que se reenvía a la AEAT.

El libro al que pertenece un registro queda fijado en el momento de la creación — book_type no puede cambiarse en un hermano A1. Cobro y Pago devuelven 422; su XSD de la AEAT no define ningún wrapper A1.

DELETE /tax_reports/{id} crea un registro hermano con operacion: B (baja / anulación). El original se enlaza mediante annulled_by_id. Cobro y Pago devuelven 422 — su XSD de la AEAT no define ningún wrapper de baja.

GET /accounts/{ACCOUNT_ID}/tax_reports devuelve los tax reports de la cuenta — SII, Verifactu, TicketBai, KSeF, todos en una sola lista. Filtra por rango de fechas u otros criterios tal como se documenta en el endpoint de listado de tax reports.


El libro SII más común. La empresa que informa es la emisora de la factura; el cliente es la contrapartida.

Ventana de terminal
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
}
]
}
}'

Campos específicos de Expedida (además de los campos CTC comunes):

CampoMapea al elemento AEATNotas
simplified_art7273FacturaSimplificadaArticulos7.2_7.3Booleano. Emite S cuando es true.
reg_previo_ggeeRegPrevioGGEEoREDEMEoCompetenciaBooleano.
macrodataMacrodatoBooleano (SII), a diferencia de la versión string de Verifactu.
facturacion_disp_adicional_mercado_gasFacturacionDispAdicionalTerceraYsextayDelMercadoOrganizadoDelGasBooleano. Exclusivo de Expedida.
sin_identif_destinatario_art_6_1_dFacturaSinIdentifDestinatarioAritculo6.1.dBooleano. Exclusivo de Expedida.
succession_party_name + _tax_idEntidadSucedida > NombreRazon / NIFAmbos obligatorios conjuntamente cuando se usan.
billing_agreement_numberNumRegistroAcuerdoFacturacionMáximo 15 caracteres.
external_referenceRefExternaMáximo 60 caracteres.
tax_point_dateFechaOperacionOpcional; se omite por defecto.

El wrapper XML de la AEAT es SuministroLRFacturasEmitidas. Los cinco flags S/N anteriores son opcionales y se emiten solo cuando son true.


Recibida tiene la inversión emisor/receptor respecto a Expedida: el proveedor externo es el emisor (registrado en IDEmisorFactura), y la empresa que informa es la receptora (Titular). Usa supplier_party_* para el proveedor externo; customer_party_* no es necesario (los valores por defecto de la empresa se usan como receptor).

Ventana de terminal
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
}
]
}
}'

Campos específicos de Recibida:

CampoMapea al elemento AEATNotas
supplier_party_tax_id / _nameIDEmisorFactura > NIF / Contraparte > NombreRazonEl proveedor externo — el emisor de la factura recibida.
supplier_party_countryIDOtro > CodigoPaisCuando no es es, el emisor se renderiza mediante IDOtro en lugar de NIF.
tax_point_dateFechaOperacionFecha de la operación/servicio. Se omite cuando no se facilita.
fecha_contableFechaRegContableFecha en que la factura se registró en tus libros contables. Si no está presente, usa tax_point_date y luego invoice_date como fallback.
cuota_deducible_in_centsCuotaDeducibleCuota deducible. Cuando se omite, se deriva de sum(tax_breakdowns.tax_amount). Establécela explícitamente para sobreescribir (deducibilidad parcial, etc.).
simplified_art7273FacturaSimplificadaArticulos7.2_7.3Booleano. Misma forma que Expedida.
reg_previo_ggeeRegPrevioGGEEoREDEMEoCompetenciaBooleano.
macrodataMacrodatoBooleano.
succession_party_*EntidadSucedidaIgual que Expedida.
billing_agreement_numberNumRegistroAcuerdoFacturacionIgual que Expedida.
external_referenceRefExternaIgual que Expedida.

Los dos flags exclusivos de Expedida (facturacion_disp_adicional_mercado_gas, sin_identif_destinatario_art_6_1_d) no aparecen en Recibida — el XSD de la AEAT los define solo en FacturaExpedidaType.

El wrapper XML de la AEAT es SuministroLRFacturasRecibidas.

Cuando supplier_party_country no es es, el emisor se emite como IDOtro en lugar de NIF:

<sii:IDEmisorFactura>
<sii:IDOtro>
<sii:CodigoPais>FR</sii:CodigoPais>
<sii:IDType>04</sii:IDType>
<sii:ID>FR12345678901</sii:ID>
</sii:IDOtro>
</sii:IDEmisorFactura>

El bloque Contraparte también se renderiza con IDOtro para que coincida.


Otros libros (Inversion, Intracomunitaria, Metalico, Seguro, Viajesagencia, Cobro, Pago)

Sección titulada «Otros libros (Inversion, Intracomunitaria, Metalico, Seguro, Viajesagencia, Cobro, Pago)»

Los siete libros restantes cubren cada uno un requisito de información específico. Comparten los campos CTC comunes y el mismo ciclo de vida, pero llevan escalares específicos del libro. Consulta la referencia OpenAPI para la lista completa de campos.

Registra la prorrata anual + la regularización de los bienes de inversión. Wrapper AEAT: SuministroLRBienesInversion. Campos obligatorios (además del wrapper CTC común):

  • identificacion_bien — identificador del bien (IdentificacionBien, máx. 40 caracteres)
  • fecha_inicio_utilizacion — fecha en que el bien entró en uso (FechaInicioUtilizacion)
  • prorrata_anual_definitiva — prorrata anual definitiva (ProrrataAnualDefinitiva, 0–100, 2 decimales)

Opcionales: regularizacion_anual_deduccion_in_cents, identificacion_entrega, regularizacion_deduccion_efectuada_in_cents, external_reference, billing_agreement_number, succession_party_*.

Intracomunitaria (operaciones intracomunitarias)

Sección titulada «Intracomunitaria (operaciones intracomunitarias)»

Wrapper AEAT: SuministroLRDetOperacionIntracomunitaria. Obligatorios: clave_declarado (enum D o R), external_reference (por el XSD de la AEAT), más description mapeado a DescripcionBienes. Opcionales: plazo_operacion, facturas_o_documentacion.

Metalico / Seguro / Viajesagencia (agregados por período)

Sección titulada «Metalico / Seguro / Viajesagencia (agregados por período)»

Estos libros se agregan por período: un solo registro representa el total de una contrapartida en un período fiscal. No hay invoice_number ni invoice_date — el XSD de la AEAT omite el bloque IDFactura. La contrapartida se facilita mediante customer_party_* (B2Brouter lo refleja en nombre_contraparte en el servidor).

  • Metalico: wrapper SuministroLRCobrosMetalico. Agrega los cobros en efectivo de una contrapartida ≥ 6.000 € en un período fiscal.
  • Seguro: wrapper SuministroLROperacionesSeguros. Añade un ClaveOperacion obligatorio mapeado desde operation_type.
  • Viajesagencia: wrapper SuministroLRAgenciasViajes. Estructuralmente idéntico a Metalico según el XSD de la AEAT.

Cobro: cobros recibidos de facturas emitidas. Pago: pagos realizados de facturas recibidas. Ambos llevan un array de pagos por registro en 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..."
}
]
}
}

(Usa pagos en lugar de cobros para Pago.)

No existen variantes A1 ni B para estos libros según el XSD de la AEAT — PATCH y DELETE devuelven 422.


Para integraciones de alto volumen (agregaciones de punto de venta, migraciones CSV a JSON, etc.), usa POST /accounts/{ACCOUNT_ID}/tax_report_batches para enviar hasta 5.000 registros en una sola petición. El procesamiento es asíncrono: la llamada valida solo el wrapper y devuelve 202 Accepted con un id de lote; la construcción, validación y asignación al ledger de cada registro se ejecutan en un proceso en segundo plano. Luego debes consultar el endpoint de estado del lote (o confiar en el webhook tax_report.state_change por registro cuando la AEAT responda).

Ventana de terminal
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", ... },
...
]
}'

Respuesta aceptada (202):

{
"batch_id": 4567,
"status": "processing",
"total": 2,
"status_url": "https://api-staging.b2brouter.net/accounts/{ACCOUNT_ID}/tax_report_batches/4567"
}

Consulta el estado del lote en GET /accounts/{ACCOUNT_ID}/tax_report_batches/{BATCH_ID}. Mientras el proceso se ejecuta, status es "processing"; una vez finalizado es "done" y results se rellena, en orden de envío:

{
"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, ... } }
]
}

Semántica:

  • Asíncrono: el 202 significa únicamente que el lote ha sido aceptado para su procesamiento — no que se haya creado ningún registro. Consulta status_url hasta que status sea "done", o consume el webhook tax_report.state_change para el resultado de la AEAT de cada registro creado.
  • Éxito parcial: cada registro se valida independientemente en el proceso. Un error en un registro (mal book_type, XML inválido según XSD, type no soportado) no aborta el lote — aparece como error por registro en results.
  • Errores de wrapper (clave tax_reports ausente, no es un array, más de 5.000 registros) devuelven HTTP 400 de forma síncrona, antes de que nada se encole.
  • Solo SII en v1: este endpoint acepta únicamente type: "SiiDocument". Un type no SII o ausente en cualquier registro se rechaza síncronamente con HTTP 422 (code: "unsupported_type") antes de que nada se encole — es un error de contrato de endpoint, distinto de los errores de datos SII por registro (mal book_type, XML inválido por XSD), que se notifican como errores parciales en el estado del lote.
  • Asignación a ledger + envío: los registros creados se asignan a ledgers SII segregados por modo agrupados por (document_type_code, NIF de la empresa informadora) — es decir, por el Titular declarado en la Cabecera de la AEAT, no por el supplier_party_tax_id por registro. Para Recibida/Pago ese campo contiene el proveedor externo, por lo que los registros de distintos proveedores comparten un ledger siempre que pertenezcan al mismo libro y empresa informadora. Los ledgers se envían a la AEAT por el cron por minutos (consulta la API de Ledgers) — nunca en el hilo de la petición.

Dos opciones, usa una o ambas:

  • Polling a status_url hasta que status sea "done".

  • Webhook — suscribe un webhook habilitado en tu grupo de integración al evento sii_batch.finished. Cuando el proceso de ingesta finaliza, B2Brouter hace un POST:

    {
    "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"
    }
    }

    El cuerpo del webhook contiene solo el resumen (acotado independientemente del tamaño del lote); llama a GET status_url para los resultados completos por registro. El resultado de la AEAT de cada registro creado sigue llegando mediante su propio webhook tax_report.state_change.

Los tax reports SII no se envían individualmente a la AEAT — se agrupan en “Libros de Registro” (Ledger en términos de B2Brouter), que contienen hasta 5.000 registros por libro / empresa informadora (Titular). Cada tax report tiene un campo ledger_id que apunta al ID interno del Ledger. Con ese ID puedes:

Los Ledgers SII modernos se vacían a la AEAT exclusivamente por el cron por minutos impulsado por la política Strategy::Sii (60 segundos entre envíos, o inmediatamente una vez que un Ledger alcanza los 5.000 registros). Ni POST /tax_reports, PATCH/DELETE, ni el endpoint masivo envían nunca a la AEAT en el hilo de la petición — asignan el/los registro/s y devuelven; el cron hace el envío. Prevé hasta ~60 s entre la creación y el envío.

Equivalencia entre los campos internos de tax report de B2Brouter y los nodos XML del SII

Sección titulada «Equivalencia entre los campos internos de tax report de B2Brouter y los nodos XML del SII»
Campo de B2BrouterNodo XML de la AEAT
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 (se omite cuando es cero)
Campo de B2BrouterNodo XML de la AEAT
fiscal_yearPeriodoLiquidacion > Ejercicio
fiscal_periodPeriodoLiquidacion > Periodo
supplier_party_tax_idIDFactura > IDEmisorFactura > NIF (o IDOtro > ID para no-ES)
supplier_party_countryIDFactura > IDEmisorFactura > IDOtro > CodigoPais (solo no-ES)
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 (se omite cuando es cero)
fecha_contableFacturaRecibida > FechaRegContable
cuota_deducible_in_centsFacturaRecibida > CuotaDeducible

category del desglose fiscal → bucket de desglose

Sección titulada «category del desglose fiscal → bucket de desglose»

Cada tax_breakdowns[*].category (el código de categoría de IVA estándar PEPPOL/UN-CEFACT) determina en qué bucket de desglose de la AEAT aterriza la línea. Esta es la señal canónica — envía la category correcta; los flags booleanos se derivan de ella.

Expedida (TipoDesglose > DesgloseFactura):

categoryBucket AEAT
S, Z, H, AA, AAA, N1 (sujeto)Sujeta > NoExenta, TipoNoExenta = S1
AE (inversión del sujeto pasivo)Sujeta > NoExenta, TipoNoExenta = S2
E, K, N2.*, N3.2, N3.6, N4, N5, N7, IC (exento)Sujeta > Exenta > DetalleExenta (CausaExencionexemption_code)
NS, O, G, N3.1, N3.3N3.5 (no sujeto)NoSujeta (ImporteTAIReglasLocalizacion cuando no_subject_code = RL, si no ImportePorArticulos7_14_Otros)

Una combinación de líneas S1 y S2 colapsa a un único TipoNoExenta = S3. Una línea sin categoría reconocida se renderiza como S1 (totalmente sujeta).

Recibida (DesgloseFactura): las líneas con inversión del sujeto pasivo (category AE) van a InversionSujetoPasivo; el resto a DesgloseIVA.

Aún no soportado: DesgloseTipoOperacion (la división bienes/servicios que la AEAT requiere para algunas contrapartidas transfronterizas). Las facturas que lo necesiten deben usar la ruta CSV heredada hasta que una versión posterior añada el soporte — refleja el aplazamiento de DatosInmueble para bienes inmuebles.

La AEAT publica los XSD canónicos del SII. B2Brouter valida el XML generado contra SuministroInformacion.xsd y SuministroLR.xsd (v1.1) en cada envío. Los esquemas se encuentran en este repositorio en vendor/xsd-validator/lib/xsd/schemas/sii_v11/.

Descripción de los códigos para campos específicos

Sección titulada «Descripción de los códigos para campos específicos»

Los valores se listan en ¿Qué es el SII? más arriba. El book_type determina el wrapper XML de la AEAT, el conjunto de campos obligatorios y las reglas de validación.

Para Expedida y 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)

Sección titulada «special_regime_key (ClaveRegimenEspecialOTrascendencia)»

Un código de dos dígitos de la AEAT que describe el régimen especial aplicado a la operación. Los valores más comunes son 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). Consulta las especificaciones de la AEAT para la lista completa y la aplicabilidad por libro.

  • D — Declarante
  • R — Receptor

B2Brouter valida los payloads SII contra los XSD de la AEAT antes de cualquier envío. Un error de validación devuelve HTTP 422 con los campos que fallan detallados en JSON. Si la AEAT rechaza un envío (p. ej. NIF de contrapartida desconocido), el rechazo se refleja en el state del registro (normalmente error o registered_with_errors) y en los campos error_code / error_description de la AEAT. Configura el webhook de tax report para reaccionar a los estados finales sin polling.