Validate Invoice Data
Pre-validate your invoice JSON before generating a PDF. This endpoint checks for errors without consuming quota or generating a PDF.
Endpoint
POST https://api.thelawin.dev/v1/validatePurpose
Use this endpoint to:
- Check for validation errors before calling
/v1/generate - Detect the format that will be used (with
format: "auto") - Get legal warnings for country-specific requirements
- Avoid wasting quota on invalid invoices
Free Pre-Validation
This endpoint does NOT generate a PDF and does NOT count against your quota. Use it liberally!
Headers
| Header | Required | Description |
|---|---|---|
Content-Type | Yes | application/json |
No API Key Required
Pre-validation is free and does not require an API key.
Request Body
The request body is identical to /v1/generate:
{
"format": "auto",
"profile": "en16931",
"template": "minimal",
"invoice": {
"number": "2026-001",
"date": "2026-01-15",
"seller": {
"name": "Acme GmbH",
"vat_id": "DE123456789",
"city": "Berlin",
"country": "DE"
},
"buyer": {
"name": "Customer AG",
"city": "München",
"country": "DE"
},
"items": [{
"description": "Consulting",
"quantity": 8,
"unit": "HUR",
"unit_price": 150,
"vat_rate": 19
}]
}
}See Generate API Parameters for all available fields.
Response
Valid (200)
{
"valid": true,
"format": {
"format_used": "zugferd",
"profile": "EN16931",
"version": "2.3",
"format_reason": "Auto-detected based on seller.country=DE",
"warnings": [
{
"code": "DE_B2B_XRECHNUNG_ALTERNATIVE",
"message": "XRechnung format is available for German B2B/B2G transactions",
"legal_basis": "§ 27 Abs. 38 UStG, E-Rechnungs-Verordnung",
"severity": "info"
}
]
},
"errors": []
}Invalid (200)
{
"valid": false,
"format": {
"format_used": "zugferd",
"profile": "EN16931"
},
"errors": [
"invoice.seller.vat_id: VAT ID required for ZUGFeRD EN16931 profile",
"invoice.buyer.country: Invalid country code 'XX'",
"invoice.items[0].unit_price: Unit price must be positive"
]
}Response Fields
| Field | Type | Description |
|---|---|---|
valid | boolean | Whether the invoice data is valid |
format | object | Format detection result |
format.format_used | string | Detected format (zugferd, xrechnung, etc.) |
format.profile | string | Profile name (e.g., EN16931) |
format.version | string | Format version (e.g., 2.3) |
format.format_reason | string | Why this format was chosen (for auto mode) |
format.warnings | array | Legal warnings for country-specific requirements |
errors | array | Validation error messages (empty if valid) |
Validation Rules
The validator checks for:
EN 16931 Requirements
- Invoice number (unique identifier)
- Invoice date (YYYY-MM-DD format)
- Seller name and VAT ID
- Buyer name and country
- At least one line item
- Valid unit codes (UN/ECE Rec 20)
- Valid country codes (ISO 3166-1 alpha-2)
Format-Specific Requirements
ZUGFeRD/Factur-X:
seller.vat_idrequired for EN16931 profile
XRechnung:
leitweg_idrequired (format:<Behördenkennung>-<Empfänger>-<Leitweg>)buyer_referencerecommended
Peppol:
seller.peppol_idandbuyer.peppol_idrequiredbuyer_referencemandatory- Peppol ID format:
<EAS code>:<identifier>
FatturaPA:
tipo_documentorequired (TD01, TD04, etc.)seller.codice_fiscaleandbuyer.codice_fiscalerequiredbuyer.codice_destinatarioORbuyer.pecrequired
See Invoice Formats for complete requirements.
Legal Warnings
The API returns legal warnings for country-specific regulations:
Examples
IT_SDI_REQUIRED
{
"code": "IT_SDI_REQUIRED",
"message": "Italian invoices must be submitted via Sistema di Interscambio (SDI)",
"legal_basis": "Art. 1, comma 3, D.Lgs. 127/2015",
"severity": "warning"
}DE_B2G_XRECHNUNG_MANDATORY
{
"code": "DE_B2G_XRECHNUNG_MANDATORY",
"message": "XRechnung format is mandatory for German B2G invoices since 2020-11-27",
"legal_basis": "E-Rechnungs-Verordnung (ERechV), § 27 Abs. 38 UStG",
"severity": "warning"
}PEPPOL_NETWORK_REGISTRATION
{
"code": "PEPPOL_NETWORK_REGISTRATION",
"message": "Peppol invoices require sender/receiver registration in Peppol network",
"legal_basis": "Peppol Transport Infrastructure Specifications",
"severity": "info"
}Usage Examples
curl
curl -X POST https://api.thelawin.dev/v1/validate \
-H "Content-Type: application/json" \
-d '{
"format": "auto",
"invoice": {
"number": "2026-001",
"date": "2026-01-15",
"seller": {
"name": "Acme GmbH",
"city": "Berlin",
"country": "DE"
},
"buyer": {
"name": "Customer AG",
"city": "München",
"country": "DE"
},
"items": [{
"description": "Consulting",
"quantity": 8,
"unit": "HUR",
"unit_price": 150
}]
}
}'JavaScript
async function validateInvoice(invoiceData) {
const response = await fetch('https://api.thelawin.dev/v1/validate', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
format: 'auto',
invoice: invoiceData
})
});
const result = await response.json();
if (result.valid) {
console.log('✓ Valid invoice');
console.log('Format:', result.format.format_used);
if (result.format.warnings.length > 0) {
console.warn('Warnings:', result.format.warnings);
}
} else {
console.error('✗ Validation failed');
console.error('Errors:', result.errors);
}
return result;
}
// Usage
const result = await validateInvoice({
number: '2026-001',
date: '2026-01-15',
seller: { name: 'Acme GmbH', city: 'Berlin', country: 'DE' },
buyer: { name: 'Customer AG', city: 'München', country: 'DE' },
items: [{
description: 'Consulting',
quantity: 8,
unit: 'HUR',
unit_price: 150
}]
});Python
import requests
def validate_invoice(invoice_data):
response = requests.post(
'https://api.thelawin.dev/v1/validate',
json={
'format': 'auto',
'invoice': invoice_data
}
)
result = response.json()
if result['valid']:
print(f"✓ Valid invoice")
print(f"Format: {result['format']['format_used']}")
if result['format']['warnings']:
print(f"Warnings: {result['format']['warnings']}")
else:
print(f"✗ Validation failed")
for error in result['errors']:
print(f" - {error}")
return result
# Usage
result = validate_invoice({
'number': '2026-001',
'date': '2026-01-15',
'seller': {'name': 'Acme GmbH', 'city': 'Berlin', 'country': 'DE'},
'buyer': {'name': 'Customer AG', 'city': 'München', 'country': 'DE'},
'items': [{
'description': 'Consulting',
'quantity': 8,
'unit': 'HUR',
'unit_price': 150
}]
})Workflow
Recommended workflow for invoice generation:
graph LR
A[Build Invoice JSON] --> B[POST /v1/validate]
B --> C{Valid?}
C -->|Yes| D[POST /v1/generate]
C -->|No| E[Fix Errors]
E --> B
D --> F[Save PDF]- Build your invoice JSON
- Validate with
/v1/validate - Check for errors
- Fix any validation errors
- Generate with
/v1/generate - Save the resulting PDF
Benefits
| Benefit | Description |
|---|---|
| Free | No quota consumption, no API key required |
| Fast | No PDF generation, instant response |
| Format Detection | Know which format will be used |
| Legal Warnings | Get country-specific compliance info |
| Error Prevention | Fix errors before wasting quota |
Common Errors
Missing VAT ID
{
"valid": false,
"errors": [
"invoice.seller.vat_id: VAT ID required for ZUGFeRD EN16931 profile"
]
}Solution: Add seller.vat_id with format CC123456789 (e.g., DE123456789)
Invalid Country Code
{
"valid": false,
"errors": [
"invoice.buyer.country: Invalid country code 'XX'"
]
}Solution: Use ISO 3166-1 alpha-2 codes (e.g., DE, FR, IT)
Invalid Unit Code
{
"valid": false,
"errors": [
"invoice.items[0].unit: Invalid unit code 'HOUR'"
]
}Solution: Use UN/ECE unit codes (e.g., HUR for hours, DAY for days)
Further Reading
- Generate API - Generate invoices
- Invoice Formats - All supported formats
- Unit Codes - Valid UN/ECE unit codes
- Error Handling - Error codes and solutions