Validations
The SDK enforces locally every validation listed in Validaciones y errores del servicio v1.2.2. This lets you catch malformed records before spending a SOAP call — the AEAT returns the same diagnostics it would have returned, but in microseconds.
Calling the validator
import {
validateInvoiceForRegister,
validateInvoiceForCancel,
} from 'verifactu-sdk/validators';
const issues = validateInvoiceForRegister(invoice);
if (issues.some((i) => i.severity === 'rejection')) {
throw new BusinessValidationError('Invoice rejected', { code: issues[0]!.code });
}Each returned ValidationResult carries:
code— the closest matching AEAT error code from the catalog.field— dotted path of the offending field (e.g.breakdown.0.taxRate).severity—'rejection'rejects the record,'admissible'accepts it with a warning.message— short English description of the violation.
VerifactuClient.registerInvoice runs the validator automatically and throws BusinessValidationError on rejection; you only call the validator directly when you want to inspect every issue at once.
The 23 rules
The validator implements one or more checks per AEAT rule. The mapping is:
| Rule | Field group | What it checks | Severity |
|---|---|---|---|
| 1 | IDFactura | NIF format, NumSerieFactura allowed chars, FechaExpedicion ≥ 28-10-2024 and ±20 years from today. | Rejection |
| 2 | RechazoPrevio + Subsanacion | Coherent combinations only (N, S, X). | Rejection |
| 3 | TipoRectificativa | Mandatory iff TipoFactura starts with R. | Rejection |
| 4 | FacturasRectificadas | Only allowed on R-type invoices. | Rejection |
| 5 | FacturasSustituidas | Only allowed on F3 invoices. | Rejection |
| 6 | ImporteRectificacion | Required when TipoRectificativa = S. | Rejection |
| 7 | FechaOperacion | ±20 years vs. issue date, special rules for ClaveRegimen 14/15. | Rejection |
| 8 | FacturaSimplificadaArt7273 | Only on F1/F3/R1/R2/R3/R4. | Rejection |
| 9 | FacturaSinIdentifDestinatarioArt61d | Only on F2/R5. | Rejection |
| 10 | Macrodato | Mandatory when ImporteTotal ≥ 100 000 000 €. | Rejection |
| 11 | EmitidaPorTerceroODestinatario | Coherence with Tercero / Destinatarios. | Rejection |
| 12 | Tercero | NIF format, distinct from emitter, NIF-IVA per country structure. | Rejection |
| 13 | Destinatarios | Required for F1/F3/R1/R2/R3/R4; forbidden for F2/R5; NIF format; NIF-IVA structure. | Rejection |
| 14 | Cupon | Only allowed when TipoFactura is R1 or R5. | Rejection |
| 15.1 | Desglose.TipoImpositivo | Allowed rate by date (per AEAT historic-rate table). | Rejection |
| 15.2 | BaseImponibleACoste | Only allowed when ClaveRegimen = 06 or Impuesto in 02/05. | Rejection |
| 15.3 | TipoRecargoEquivalencia | Valid pair with TipoImpositivo (21/5.2, 10/1.4, …). | Rejection |
| 15.4 | CalificacionOperacion | S1/S2/N1/N2; mutual exclusion with OperacionExenta. | Rejection |
| 15.5 | OperacionExenta | E1–E6 (IVA); E7/E8 only for IGIC; coherence with TipoFactura. | Rejection |
| 15.6 | ClaveRegimen | 01–21 with sub-rules for 02/03/04/06/07/08/10/11/14/20/21. | Rejection |
| 15.7 | CuotaRepercutida | base × rate with ±10 € tolerance (≤ 3 000 € for simplified). | Rejection |
| 16 | CuotaTotal | Sum of repercussed amounts with ±10 € tolerance. | Rejection |
| 17 | ImporteTotal | Sum of bases + tax + surcharge with ±10 € tolerance. | Rejection |
| 18 | Huella anterior | 64 uppercase hex chars when present. | Admissible (2003) |
| 19 | SistemaInformatico | SystemId 2 chars [A-Z0-9] excluding Ñ; TipoUsoPosibleSoloVerifactu / MultiOT coherence. | Rejection |
| 20 | FechaHoraHusoGenRegistro | Valid ISO 8601 with offset; not in the far future. | Rejection / Admissible |
| 21 | NumRegistroAcuerdoFacturacion | ≤ 15 chars, allowed character set. | Rejection |
| 22 | IdAcuerdoSistemaInformatico | ≤ 16 chars, allowed character set. | Rejection |
| 23 | Huella | 64 uppercase hex chars. | Admissible (1292) |
NIF, NIE, CIF
The local validator covers the three Spanish identifier shapes and the check digit:
- DNI: 8 digits + control letter computed from the modulo-23 table.
- NIE:
X|Y|Zprefix + 7 digits + control letter, same table. - CIF: letter + 7 digits + control digit/letter (mixed modulo-10 and modulo-11 algorithm per AEAT spec).
NIF-IVA (EU)
The validator covers the 28 historic EU member states plus Northern Ireland (GB/XI) with Brexit-aware date handling. The structures and the per-country rules are loaded from validators/nifIva.ts. Examples:
| Country | Code | Structure |
|---|---|---|
| Germany | DE | 9 digits |
| France | FR | 11 chars (2 alphanumeric + 9 digits) |
| Italy | IT | 11 digits |
| Portugal | PT | 9 digits |
| Netherlands | NL | 12 chars |
| UK | GB | 9 / 12 digits; valid only ≤ 31-12-2020 (post-Brexit, use XI) |
| Northern Ireland | XI | 9 / 12 digits; valid only ≥ 01-01-2021 |
The validator accepts country=ES for completeness, but Spanish NIFs go through the local NIF/CIF path instead.
Severity
The AEAT distinguishes three severities:
envelope— the entire submission is rejected (codes 4xxx, set by the service after parsing the SOAP envelope).record— only the offending record is rejected (codes 1xxx and 3xxx).admissible— the record is accepted but flagged for subsanación (codes 2xxx).
The local validator returns the first two as severity: 'rejection' and the third as severity: 'admissible'. Envelope errors come from the wire layer and never appear in the local result.
Next
- Error codes — full catalog of codes with the verbatim Spanish text from
errores.properties. - Hash chain