Tell a voided invoice from a totally rectified one, without a second call
Every voided invoice now carries void_cause, void_reason and voided_at. VOIDED was reachable two ways — a direct void, or a TOTAL corrective issued over the invoice — and until now telling them apart meant looking for a corrective invoice yourself. The fields come back on the invoice object, both on the single read and on every row of the list.
Every voided invoice now carries void_cause, void_reason and voided_at. VOIDED was reachable two ways — a direct void, or a TOTAL corrective issued over the invoice — and until now telling them apart meant looking for a corrective invoice yourself. The fields come back on the invoice object, both on the single read and on every row of the list.
What else changed
void_causeisVOID_REQUEST(voided directly viaPOST /v1/companies/{company_id}/invoices/{invoice_id}/void) orTOTAL_CORRECTIVE(left voided by a TOTAL corrective invoice issued over it). Only present on voided invoices.- No extra call. It ships in the same response as the invoice — including each row of
GET /v1/companies/{company_id}/invoices— so you no longer need a second request to find out whether a corrective exists. void_reasonis the reason recorded at the time, andvoided_atthe exact instant of the void.- Do not read the VeriFactu state to tell the two apart. They are different tax events: a void reports the cancellation of the original record to the AEAT, while a TOTAL corrective cancels nothing — it reports a new record (R1–R5) and leaves the original as it was. The reporting state of the original invoice therefore varies by case and is not a reliable signal;
void_causeis. void_dateon the void request no longer has any effect. A void is a system instant, not a date you choose:voided_atis set at the moment it happens. Invoices voided before this field existed carry their void day at00:00Z, because only the day was kept for them.
Endpoints
- GET/v1/companies/{company_id}/invoices/{invoice_id}Returns void_cause, void_reason and voided_at on voided invoices
- GET/v1/companies/{company_id}/invoicesEvery row carries the same fields — no second call to detect a corrective
- POST/v1/companies/{company_id}/invoices/{invoice_id}/voidSets void_cause to VOID_REQUEST; void_date in the body is ignored
- POST/v1/companies/{company_id}/invoices/{invoice_id}/correctiveA TOTAL corrective sets void_cause to TOTAL_CORRECTIVE on the original invoice
Where to go next
Unknown query parameters stop being ignored on 2026-09-03
From 2026-09-03 the flat routes reject a query parameter they do not declare with `400`, instead of silently ignoring it. Until then every affected response carries a `BeeL-Warning` header naming the parameter, so you can find them before the date. The rate limits you are actually subject to are now written down.
One shape for lists, one rule for deletes, Location on every create
Every list now answers `data.<plural>` (+ `data.pagination` where the server paginates), deletes answer `204` unless the body genuinely informs, `DELETE products/bulk` takes `?ids=` instead of a body, and every single-resource `201` carries a `Location` header to its canonical GET.