VeriFactu record subsanación
Fix and resubmit VeriFactu billing records rejected by the AEAT — when subsanación applies, when you need an annulment or a corrective instead, and the exact API flow.
When the AEAT rejects a VeriFactu billing record because of a data error
(record status: rejected), the VeriFactu regulation (Royal Decree 1007/2023,
art. 11) lets you subsanar the record: resubmit the same record with
the corrected content. It is not a new invoice, not a corrective and not an
annulment — the rejected record itself is repaired and transmitted again.
POST /v1/verifactu/records/{record}/subsanar- Scope:
verifactu:write - Request body: none — the subsanable content is regenerated server-side from the source invoice and the current master data.
- Response:
202 Accepted— the resubmission is queued and sent to the AEAT within seconds.
Subsanación never recalculates the original fingerprint (huella), the previous-record chain link or the original generation timestamp: the AEAT matches the resubmission to the rejected record precisely because they are preserved. There is no cap on subsanación attempts — the technical retry cap applies only to blind retries of transmission failures.
When to use it — and when not
| Situation | What to do |
|---|---|
Record rejected by the AEAT for a data error — recipient NIF not identified in the census, wrong recipient name, description issues… | Subsanación. Fix the data at its source, then call POST …/subsanar. |
Record in error (technical transmission failure: timeout, AEAT down). | Nothing — transmission retries automatically (manual retry available). Subsanación answers record_not_rejected. |
Record accepted but the invoice carries wrong data. | A corrective invoice (R1–R5). An accepted record is immutable — subsanación answers record_not_rejected. |
| The correction changes a fingerprint field: issuer NIF, series + number, issue date, invoice type, total tax amount or total amount. | Annulment + new record (new invoice or corrective). Subsanación answers requires_annulment without modifying anything. |
The flow
Detect the rejection. Subscribe to the invoice.verifactu_failed
webhook event, or poll the
record list
for status: rejected. The record carries the AEAT rejection detail.
Fix the data at its source. The resubmitted content is regenerated from the source invoice and the current master data — e.g. correct the client's NIF or registered name and the new values are picked up automatically. The invoice's frozen legal snapshot is deliberately refreshed so the PDF matches what the AEAT receives.
Call the endpoint. The record re-enters the transmission queue with a fresh round of attempts:
curl -X POST https://api.factuarea.com/v1/verifactu/records/0197a2a8-4cf0-7a31-9a5e-3f2b8c1d6e42/subsanar \
-H "Authorization: Bearer fact_live_3pXnR2VbY7TcA9eFmN5z8KqW"Response (202):
{
"data": {
"id": "0197a2a8-4cf0-7a31-9a5e-3f2b8c1d6e42",
"message": "Subsanación encolada. El registro se reenviará a la AEAT en unos segundos."
}
}Watch the outcome. The record is transmitted again and ends accepted —
or rejected once more if the data is still wrong, in which case you can
subsanar again (there is no attempt limit).
Errors
Business-rule violations return 422 with code: business_rule_violation
and a subcode that pinpoints the cause:
{
"error": {
"type": "invalid_request_error",
"code": "business_rule_violation",
"subcode": "record_not_rejected",
"message": "El registro #842 no está rechazado por la AEAT (estado actual: accepted). Solo los registros rechazados admiten subsanación; para fallos técnicos usa el reintento."
}
}| HTTP | code / subcode | When |
|---|---|---|
| 404 | resource_not_found | The record doesn't exist or belongs to another company. |
| 422 | business_rule_violation / record_not_rejected | The record is not in rejected (it is accepted, pending, submitted — or a technical error, which the automatic retry covers). |
| 422 | business_rule_violation / requires_annulment | The correction touches fingerprint fields. Annul the record and issue a new invoice or a corrective. |
| 422 | business_rule_violation / record_not_subsanable | The record is not an alta record, or it has no source invoice to regenerate from. |
| 403 | insufficient_scope | The key lacks the verifactu:write scope. |
Avoid the rejection in the first place
The most frequent data rejection is a recipient that the AEAT census does not
identify (rejection 1239). Verify the name + NIF pair of a client with
POST /v1/clients/census-verification
before issuing VeriFactu invoices to them — subsanación then remains what
it should be: a safety net, not a routine.
AEAT census verification
Check your company's — and your clients' — name + NIF pair against the AEAT census before issuing VeriFactu invoices — deterministic states, sandbox magic NIFs and fail-open behavior.
FACe invoicing (B2G)
Submit FacturaE 3.2.2 invoices to FACe — DIR3 codes, signed XAdES-EPES XML, processing states, cancellation, sandbox simulation and the facturae:write scope.