Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/mdiago/VeriFactu/llms.txt

Use this file to discover all available pages before exploring further.

Submitting a new invoice through VeriFactu creates a RegistroAlta XML document, automatically chains it into the seller’s blockchain (linking its SHA-256 hash to the previous record), and posts it to the AEAT VERI*FACTU endpoint. If the AEAT does not return a valid CSV code, the library automatically rolls back the blockchain entry so the chain stays consistent.

Factura básica (F1)

The simplest submission requires an Invoice instance with at least one TaxItem, a SellerName, and an InvoiceType. Pass the Invoice to an InvoiceEntry constructor, then call .Save().
// Create an invoice instance
var invoice = new Invoice("GIT-EJ-0002", new DateTime(2024, 11, 15), "B72877814")
{
    InvoiceType = TipoFactura.F1,
    SellerName = "WEFINZ GANDIA SL",
    BuyerID = "B44531218",
    BuyerName = "WEFINZ SOLUTIONS SL",
    Text = "PRESTACION SERVICIOS DESARROLLO SOFTWARE",
    TaxItems = new List<TaxItem>()
    {
        new TaxItem()
        {
            TaxRate = 4,
            TaxBase = 10,
            TaxAmount = 0.4m
        },
        new TaxItem()
        {
            TaxRate = 21,
            TaxBase = 100,
            TaxAmount = 21
        }
    }
};

// Create the invoice entry
var invoiceEntry = new InvoiceEntry(invoice);

// Submit to AEAT
invoiceEntry.Save();

// Check the result
Debug.Print($"Estado: {invoiceEntry.Status}");

if (invoiceEntry.Status == "Correcto")
{
    Debug.Print($"CSV: {invoiceEntry.CSV}");
}
else
{
    Debug.Print($"Error: {invoiceEntry.ErrorCode}: {invoiceEntry.ErrorDescription}");
}

// Full raw response from AEAT
Debug.Print($"Respuesta: {invoiceEntry.Response}");

Tipos de factura

Set Invoice.InvoiceType to one of the TipoFactura enum values (L2 in the AEAT specification):
ValorDescripción
F1Factura completa (art. 6, 7.2 y 7.3 del RD 1619/2012).
F2Factura simplificada y facturas sin identificación del destinatario (art. 6.1.d) RD 1619/2012).
F3Factura emitida en sustitución de facturas simplificadas previamente facturadas y declaradas.
R1Factura rectificativa — error fundado en derecho y art. 80 Uno, Dos y Seis LIVA.
R2Factura rectificativa — art. 80.3 (destinatario en concurso de acreedores).
R3Factura rectificativa — art. 80.4 (créditos total o parcialmente incobrables).
R4Factura rectificativa — resto de causas.
R5Factura rectificativa en facturas simplificadas.

TaxItem: líneas de impuesto

Each entry in Invoice.TaxItems represents one tax breakdown line (DetalleDesglose in the XML). A maximum of 12 items is allowed per invoice.
PropiedadTipoDescripción
TaxBasedecimalBase imponible (o importe no sujeto).
TaxRatedecimalTipo impositivo en porcentaje (p. ej. 21 para 21%).
TaxAmountdecimalCuota repercutida.
TaxSchemeClaveRegimenClave de régimen del IVA/IGIC (L8A/L8B). Por defecto RegimenGeneral (01).
TaxTypeCalificacionOperacionCalificación de la operación sujeta y no exenta (L9). Por defecto S1.
TaxExceptionCausaExencionCausa de exención (L10). Por defecto NA (no exenta). Cuando se asigna, TaxRate, TaxAmount, TaxRateSurcharge y TaxAmountSurcharge deben ser 0.
TaxRateSurchargedecimalTipo de recargo de equivalencia (%).
TaxAmountSurchargedecimalCuota de recargo de equivalencia.
TaxImpuestoImpuesto aplicable: IVA (01), IPSI (02), IGIC (03), u Otros (05). Por defecto IVA.
TaxClassTaxClassIndica si la línea es de impuesto repercutido (TaxOutput, valor por defecto) o de retención (TaxWithheld). Afecta al cálculo de TotalAmount.

ClaveRegimen — valores principales

ValorCódigo XMLDescripción
RegimenGeneral01Operación de régimen general.
Exportacion02Exportación.
Rebu03Bienes usados, objetos de arte, antigüedades y objetos de colección.
AgenciasViajes05Régimen especial de las agencias de viajes.
Recc07Régimen especial del criterio de caja.
RecEquivPeqEmp18Recargo de equivalencia / pequeño empresario o profesional (IGIC).

CalificacionOperacion

ValorDescripción
S1Operación sujeta y no exenta — sin inversión del sujeto pasivo.
S2Operación sujeta y no exenta — con inversión del sujeto pasivo.
N1Operación no sujeta — artículo 7, 14 u otros.
N2Operación no sujeta — por reglas de localización.

CausaExencion

ValorDescripción
NANo asignada (valor por defecto, operación no exenta).
E1Exenta por el artículo 20 (exenciones en operaciones interiores).
E2Exenta por el artículo 21 (exportaciones).
E3Exenta por el artículo 22 (navegación marítima internacional, aeronaves…).
E4Exenta por los artículos 23 y 24 (regímenes aduaneros y fiscales).
E5Exenta por el artículo 25 (operaciones intracomunitarias).
E6Exenta por otros.
E7Reservado — impuesto IGIC.
E8Reservado — impuesto IGIC.

Facturas rectificativas

For invoice types R1R5, set Invoice.RectificationType (TipoRectificativa) and optionally populate RectificationItems with the invoices being corrected. TipoRectificativa has three values:
ValorDescripción
NANo asignado (valor por defecto del enum; úsese en facturas que no son rectificativas). Cuando el tipo de factura es rectificativa (R1R5) y RectificationType se deja en NA, la librería lo trata automáticamente como I.
IPor diferencias.
SPor sustitución.

Rectificativa por diferencias (tipo I)

Use TipoRectificativa.I and list the original invoice(s) in RectificationItems. The TaxItems contain the difference amounts.
var invoice = new Invoice("RECT-001", new DateTime(2024, 11, 20), "B72877814")
{
    InvoiceType = TipoFactura.R1,
    RectificationType = TipoRectificativa.I,
    SellerName = "WEFINZ GANDIA SL",
    BuyerID = "B44531218",
    BuyerName = "WEFINZ SOLUTIONS SL",
    Text = "RECTIFICACION FACTURA GIT-EJ-0002",
    RectificationItems = new List<RectificationItem>()
    {
        new RectificationItem()
        {
            InvoiceID = "GIT-EJ-0002",
            InvoiceDate = new DateTime(2024, 11, 15)
        }
    },
    TaxItems = new List<TaxItem>()
    {
        new TaxItem()
        {
            TaxRate = 21,
            TaxBase = -10,      // Negative difference
            TaxAmount = -2.1m
        }
    }
};

var invoiceEntry = new InvoiceEntry(invoice);
invoiceEntry.Save();

Rectificativa por sustitución (tipo S)

Use TipoRectificativa.S and supply the original totals via RectificationTaxBase and RectificationTaxAmount. The TaxItems contain the new, corrected amounts.
var invoice = new Invoice("RECT-002", new DateTime(2024, 11, 20), "B72877814")
{
    InvoiceType = TipoFactura.R1,
    RectificationType = TipoRectificativa.S,
    SellerName = "WEFINZ GANDIA SL",
    BuyerID = "B44531218",
    BuyerName = "WEFINZ SOLUTIONS SL",
    Text = "RECTIFICACION POR SUSTITUCION FACTURA GIT-EJ-0002",
    // Original invoice totals (from the invoice being replaced)
    RectificationTaxBase = 110,
    RectificationTaxAmount = 21.4m,
    RectificationItems = new List<RectificationItem>()
    {
        new RectificationItem()
        {
            InvoiceID = "GIT-EJ-0002",
            InvoiceDate = new DateTime(2024, 11, 15)
        }
    },
    // New corrected tax breakdown
    TaxItems = new List<TaxItem>()
    {
        new TaxItem()
        {
            TaxRate = 21,
            TaxBase = 100,
            TaxAmount = 21
        }
    }
};

var invoiceEntry = new InvoiceEntry(invoice);
invoiceEntry.Save();

Comprobar la respuesta

After calling .Save(), inspect these properties on the InvoiceEntry instance:
PropiedadTipoDescripción
StatusstringEstado del resultado. "Correcto" si el registro fue aceptado.
CSVstringCódigo Seguro de Verificación devuelto por la AEAT. Vacío si hubo error.
ErrorCodestringCódigo de error devuelto por la AEAT (cuando Status != "Correcto").
ErrorDescriptionstringDescripción del error devuelto por la AEAT.
ResponsestringRespuesta XML completa de la AEAT.
invoiceEntry.Save();

if (invoiceEntry.Status == "Correcto")
{
    Console.WriteLine($"Registrada correctamente. CSV: {invoiceEntry.CSV}");
}
else
{
    Console.WriteLine($"Error {invoiceEntry.ErrorCode}: {invoiceEntry.ErrorDescription}");
    Console.WriteLine($"Respuesta completa:\n{invoiceEntry.Response}");
}
If CSV is empty after .Save() — meaning the AEAT did not accept the record — the library automatically rolls back the blockchain entry: it removes the record from the blockchain, moves the invoice file to an error path, and sets Posted = false. This keeps the blockchain consistent and allows the invoice to be resubmitted with InvoiceFix once the underlying issue is corrected.

Validación de reglas de negocio

The InvoiceEntry constructor (via InvoiceAction) calls GetBusErrors() immediately and throws an InvalidOperationException if any rule is violated. You can call GetBusErrors() yourself before construction to inspect errors without triggering an exception:
// Build a temporary action to pre-validate
var invoice = new Invoice("GIT-EJ-0010", new DateTime(2024, 11, 15), "B72877814")
{
    // SellerName intentionally missing to trigger a validation error
    TaxItems = new List<TaxItem>()
    {
        new TaxItem() { TaxRate = 21, TaxBase = 100, TaxAmount = 21 }
    }
};

// InvoiceEntry constructor will throw because SellerName is empty:
// "Es necesario que la propiedad Invoice.SellerName tenga un valor."
try
{
    var invoiceEntry = new InvoiceEntry(invoice);
}
catch (InvalidOperationException ex)
{
    Console.WriteLine($"Validación fallida: {ex.Message}");
}
Key validation rules enforced automatically:
  • Invoice.SellerName must not be null or empty.
  • Invoice.TaxItems must not contain more than 12 items.
  • Invoice.RectificationItems must not contain more than 1 000 items.
  • An invoice with the same SellerID, year, and InvoiceID must not already exist on disk.
  • TaxItem lines with a TaxException set must have TaxRate, TaxAmount, TaxRateSurcharge, and TaxAmountSurcharge all equal to 0.

Build docs developers (and LLMs) love