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):
| Valor | Descripción |
|---|
F1 | Factura completa (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 previamente facturadas y declaradas. |
R1 | Factura rectificativa — error fundado en derecho y art. 80 Uno, Dos y Seis LIVA. |
R2 | Factura rectificativa — art. 80.3 (destinatario en concurso de acreedores). |
R3 | Factura rectificativa — art. 80.4 (créditos total o parcialmente incobrables). |
R4 | Factura rectificativa — resto de causas. |
R5 | Factura 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.
| Propiedad | Tipo | Descripción |
|---|
TaxBase | decimal | Base imponible (o importe no sujeto). |
TaxRate | decimal | Tipo impositivo en porcentaje (p. ej. 21 para 21%). |
TaxAmount | decimal | Cuota repercutida. |
TaxScheme | ClaveRegimen | Clave de régimen del IVA/IGIC (L8A/L8B). Por defecto RegimenGeneral (01). |
TaxType | CalificacionOperacion | Calificación de la operación sujeta y no exenta (L9). Por defecto S1. |
TaxException | CausaExencion | Causa de exención (L10). Por defecto NA (no exenta). Cuando se asigna, TaxRate, TaxAmount, TaxRateSurcharge y TaxAmountSurcharge deben ser 0. |
TaxRateSurcharge | decimal | Tipo de recargo de equivalencia (%). |
TaxAmountSurcharge | decimal | Cuota de recargo de equivalencia. |
Tax | Impuesto | Impuesto aplicable: IVA (01), IPSI (02), IGIC (03), u Otros (05). Por defecto IVA. |
TaxClass | TaxClass | Indica 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
| Valor | Código XML | Descripción |
|---|
RegimenGeneral | 01 | Operación de régimen general. |
Exportacion | 02 | Exportación. |
Rebu | 03 | Bienes usados, objetos de arte, antigüedades y objetos de colección. |
AgenciasViajes | 05 | Régimen especial de las agencias de viajes. |
Recc | 07 | Régimen especial del criterio de caja. |
RecEquivPeqEmp | 18 | Recargo de equivalencia / pequeño empresario o profesional (IGIC). |
CalificacionOperacion
| Valor | Descripción |
|---|
S1 | Operación sujeta y no exenta — sin inversión del sujeto pasivo. |
S2 | Operación sujeta y no exenta — con inversión del sujeto pasivo. |
N1 | Operación no sujeta — artículo 7, 14 u otros. |
N2 | Operación no sujeta — por reglas de localización. |
CausaExencion
| Valor | Descripción |
|---|
NA | No asignada (valor por defecto, operación no exenta). |
E1 | Exenta por el artículo 20 (exenciones en operaciones interiores). |
E2 | Exenta por el artículo 21 (exportaciones). |
E3 | Exenta por el artículo 22 (navegación marítima internacional, aeronaves…). |
E4 | Exenta por los artículos 23 y 24 (regímenes aduaneros y fiscales). |
E5 | Exenta por el artículo 25 (operaciones intracomunitarias). |
E6 | Exenta por otros. |
E7 | Reservado — impuesto IGIC. |
E8 | Reservado — impuesto IGIC. |
Facturas rectificativas
For invoice types R1–R5, set Invoice.RectificationType (TipoRectificativa) and optionally populate RectificationItems with the invoices being corrected.
TipoRectificativa has three values:
| Valor | Descripción |
|---|
NA | No asignado (valor por defecto del enum; úsese en facturas que no son rectificativas). Cuando el tipo de factura es rectificativa (R1–R5) y RectificationType se deja en NA, la librería lo trata automáticamente como I. |
I | Por diferencias. |
S | Por 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:
| Propiedad | Tipo | Descripción |
|---|
Status | string | Estado del resultado. "Correcto" si el registro fue aceptado. |
CSV | string | Código Seguro de Verificación devuelto por la AEAT. Vacío si hubo error. |
ErrorCode | string | Código de error devuelto por la AEAT (cuando Status != "Correcto"). |
ErrorDescription | string | Descripción del error devuelto por la AEAT. |
Response | string | Respuesta 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.