Validation rule reference
Every rule SAFTCheck applies to your SAF-T (PT) XML, in plain language. Each page covers what AT expects, the most common ways the rule fails, and how to fix it.
XML encoding declaration (Windows-1252)
AT requires Windows-1252 encoding. UTF-8 declarations are silently rejected.
encoding.declaration
Byte Order Mark (BOM) at file start
AT rejects SAF-T files that start with a UTF-8 or UTF-16 BOM.
encoding.bom
Validate a Portuguese NIF: free checker and Mod-11 rules
Check any Portuguese NIF instantly: 9 digits, valid prefix and Mod-11 check digit. Plus how the algorithm works and the most common errors.
nif.mod11
ATCUD format: free checker and the rules since 2023
Mandatory unique-document code: a validation code of at least 8 characters, a dash, and the document number. Includes a free ATCUD checker.
atcud.format
Header dates: StartDate, EndDate, FiscalYear, DateCreated
Time-window invariants AT enforces on the SAF-T Header block.
header.dates
Inline editor: fix NIF, ATCUD and header dates in place
Edit the offending value next to the finding, re-validate immediately, no ERP round-trip needed.
feature.inline-editor
VIES cross-check: NIF registered in EU VAT exchange
Header NIF is verified against the EU VIES service and the registered name is compared with the SAF-T CompanyName.
nif.vies
Document date inside the Header reporting window
Every Invoice / Payment / WorkDocument / StockMovement date must fall inside Header.StartDate – Header.EndDate.
invoice.date.out_of_range
Block totals reconcile against child documents
NumberOfEntries must count every document (cancelled included); TotalDebit and TotalCredit must equal the line amounts without VAT, cancelled documents excluded, within 0.01.
totals.reconciliation
Foreign-currency documents need a complete <Currency> block
When a document declares a non-EUR currency, CurrencyCode + CurrencyAmount + ExchangeRate must all be present.
invoice.currency.exchange
Invoice status code and date consistency
InvoiceStatus must be one of N/S/A/R/F and InvoiceStatusDate must be >= InvoiceDate.
invoice.status
Looking for narrative deep dives instead? See the blog for end-to-end fix walkthroughs.