VeriFactu QR (ES)
The QR code a Spanish invoice has to carry: NIF, series, invoice number, date and total become the AEAT verification URL — returned as a URL, as PNG (base64) and as SVG.
/api/v1/invoice/es/verifactu-qrA QR code, not a submission to the AEAT
This endpoint builds the QR code and nothing else. It does not register the invoice with the AEAT and sends no data there — it produces the verification URL a recipient can open on their phone, and renders it.
The host inside the URL follows the service configuration, not the request. Measured on 2026-09-15, the hosted service answered with the AEAT test service (prewww2.aeat.es). If you need the production host, settle that with us before the first invoice goes to print.
Request
| Field | Type | Required | Description |
|---|---|---|---|
sellerNif | string | The issuer's NIF/CIF, e.g. B12345678. | |
invoiceSeries | string | Invoice series. Together with invoiceNumber it forms the AEAT numserie parameter; reserved characters are URL-encoded. | |
invoiceNumber | string | Invoice number within the series. | |
invoiceDate | string | Invoice date in the AEAT QR format DD-MM-YYYY — not ISO. | |
total | number | Total amount as a number. Negative totals are accepted for credit notes. | |
verifactuMode | boolean | - | Deprecated and ignored. The platform is VERI*FACTU-only, so the QR always points at the ValidarQR verification service. The field is accepted for backwards compatibility and will be removed. |
Example
1curl -X POST https://service.invoice-api.xhub.io/api/v1/invoice/es/verifactu-qr \2 -H "Authorization: Bearer sk_live_abc123..." \3 -H "Content-Type: application/json" \4 -d '{5 "sellerNif": "B12345678",6 "invoiceSeries": "A",7 "invoiceNumber": "2026-0042",8 "invoiceDate": "20-03-2026",9 "total": 5712.0010 }'Response
200 OK1{2 "url": "https://prewww2.aeat.es/wlpl/TIKE-CONT/ValidarQR?nif=B12345678&numserie=A%2F2026-0042&fecha=20-03-2026&importe=5712.00",3 "png": "iVBORw0KGgoAAAANSUhEUgAAAMQAAA...",4 "svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 49 49\" shape-rendering=\"crispEdges\">..."5}Measured on 2026-09-15: numserie is built as series, slash, invoice number (here A/2026-0042) and URL-encoded — the example in the OpenAPI file writes it without a separator. The value in the response is what counts; do not assemble the URL yourself, take the one you get.
| Field | Type | Description |
|---|---|---|
url | string | The AEAT verification URL encoded in the QR code. |
png | string | The QR code as a PNG, base64-encoded and without a data: prefix. |
svg | string | The same QR code as SVG markup. |
Error cases
| Status | Code | Description |
|---|---|---|
| 400 | Bad Request | A field is missing or has the wrong type. |
| 401 | UNAUTHORIZED | The API key is missing or invalid. |
| 403 | FORBIDDEN | The entitlement is missing: e-invoice:es:facturae:create is required. |
The Spanish invoice itself
The QR code is an addition to the document, not the document. The invoice itself comes from the Creator: for Spain it offers Facturae and PDF — the QR then goes into the PDF template.
Creator API