Invoice lifecycle
Every invoice status, which operations each one allows, and the difference between the fiscal steps (issue, void, correct) and the commercial ones (sent, paid).
An invoice moves along two kinds of steps, and it pays to keep them apart:
- Fiscal steps — issuing, voiding and correcting. They create or cancel a fiscal document, reach the AEAT when the NIF is under VeriFactu, and are irreversible. Each has its own operation.
- Commercial steps —
SENTandPAID. They record what happened with your customer. They have no fiscal effect and are set with Set the status of an invoice.
The state diagram
stateDiagram-v2 direction LR [*] --> DRAFT: create DRAFT --> SCHEDULED: schedule SCHEDULED --> DRAFT: unschedule / processed as DRAFT SCHEDULED --> ISSUED: processed as ISSUE_AND_SEND DRAFT --> ISSUED: issue DRAFT --> PAID: issue, total 0 ISSUED --> SENT: emailed / status SENT SENT --> ISSUED: status ISSUED (undo) ISSUED --> PAID: status PAID SENT --> PAID: status PAID ISSUED --> RECTIFIED: PARTIAL corrective SENT --> RECTIFIED: PARTIAL corrective PAID --> RECTIFIED: PARTIAL corrective RECTIFIED --> RECTIFIED: another PARTIAL corrective ISSUED --> VOIDED: void / TOTAL corrective SENT --> VOIDED: void / TOTAL corrective PAID --> VOIDED: void / TOTAL corrective RECTIFIED --> VOIDED: void / TOTAL corrective VOIDED --> [*]
DRAFT and SCHEDULED are the only states before the fiscal line. Everything from ISSUED on has a definitive number and can never go back to DRAFT. VOIDED is terminal.
An invoice with nothing to collect — a STANDARD, SIMPLIFIED or corrective invoice whose total_to_pay is 0 — skips ISSUED: it is issued as PAID, with payment_date equal to issue_date, and registered with the AEAT like any other. Setting it PAID or SENT afterwards is refused, as for any paid invoice.
What each status allows
The fiscal actions — edit, delete, issue, void, correct — are governed by rules, and their matrix, status by status with the error each refusal answers, is What each status allows in the rules. The commercial steps:
| Status | Schedule | Send email | Set SENT | Set PAID |
|---|---|---|---|---|
DRAFT | ✓ | — | — | — |
SCHEDULED | ✓ (move it) | — | — | — |
ISSUED | — | ✓ | ✓ | ✓ |
SENT | — | ✓ (send again) | — ¹ | ✓ |
PAID | — | ✓ | — | — ² |
RECTIFIED | — | ✓ | — | — ³ |
VOIDED | — | ✓ ⁴ | — | — |
SENTcan be undone withstatus: ISSUED, which clearssent_at. That is the only way back, and it never un-issues anything.- Payments are all or nothing: there is no partial payment and no way to un-mark a paid invoice.
- See Correcting a paid invoice.
- Voiding an invoice, or rectifying it with a
TOTALcorrective, does not change its PDF: the document stays the one that was delivered, and the new status is instatus. In the sandbox every PDF carries the test-invoice watermark.
Anything outside the tables is refused and leaves the invoice as it was. The commercial refusals:
| You try | You get |
|---|---|
| Schedule anything but a draft or a scheduled invoice | 400 INVOICE_STATUS_NOT_SCHEDULABLE |
| Email a draft or a scheduled invoice | 400 INVOICE_DRAFT_NOT_SENDABLE |
Set PAID from a status that does not allow it | 400 STATUS_NOT_MODIFIABLE |
Set SENT on a draft | 400 MARK_SENT_FROM_DRAFT_NOT_ALLOWED |
Set SENT on a scheduled invoice | 400 MARK_SENT_FROM_INVALID_STATE |
Set SENT on an invoice already sent | 400 MARK_SENT_ALREADY_SENT |
Set SENT on a paid, rectified or voided invoice | 400 MARK_SENT_FROM_LATER_STATE |
Set ISSUED on anything but a SENT invoice | 400 REVERT_ONLY_FROM_SENT |
Emailing right after issuing is not refused: while the PDF is still being generated, the send answers 202 and the email goes out as soon as the PDF is stored (see Sending email).
One refusal depends on timing rather than status:
- The same partial corrective twice in a row — same lines, moments apart — answers
422CORRECTIVE_RECENT_DUPLICATEnaming the corrective that already exists, so a retried request does not correct the invoice twice.
Issuing
Issue an invoice turns a DRAFT into an ISSUED invoice in one step: it assigns the definitive number from the series, sets issue_date to today and freezes every amount. PDF generation and the VeriFactu submission follow asynchronously, so a 200 means the invoice was accepted, not that the AEAT has registered it — the fiscal side is covered in Submission states.
You can also create and issue in one call with options.issue_directly: true on Create an invoice.
A draft has no number, so deleting it leaves no gap in the series. See Series and numbering.
Dates
| Field | Rule |
|---|---|
issue_date | Not writable. It is today when the invoice is issued — whenever the draft was created. A scheduled invoice gets its scheduled date. |
operation_date | Optional. When the operation took place, if earlier than the issue. It cannot be after the issue date (OPERATION_DATE_AFTER_ISSUE_DATE). Omitted, it is the issue date. |
due_date | Optional. Omitted, the invoice has no due date: due_date stays null, also after issuing. It cannot be before the issue date (DUE_DATE_BEFORE_ISSUE_DATE). Issuing a draft later does not move it — except on drafts generated by a recurring invoice, whose due date keeps the agreed payment term counted from the actual issue date. |
Both date rules are checked when you create or edit the draft, against today — or, on a scheduled invoice, against its scheduled date.
Scheduling: SCHEDULED vs DRAFT
A DRAFT waits for you. A SCHEDULED invoice waits for a date:
curl -X PUT https://app.beel.es/api/v1/companies/{company_id}/invoices/{invoice_id}/schedule \
-H "Authorization: Bearer $BEEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "scheduled_for": "2026-10-01", "generation_mode": "ISSUE_AND_SEND" }'scheduled_foris today or later (422SCHEDULED_DATE_IN_PASTotherwise), and becomes the invoice's issue date. Scheduled invoices are processed in a daily run early in the morning, Madrid time: an invoice scheduled for today after that run is processed, and dated, the next day.generation_modedecides what happens on that date:DRAFTreturns it toDRAFTfor you to review;ISSUE_AND_SENDissues it and emails it.- Until then it stays editable and deletable.
PUT …/scheduleagain moves it; Remove the scheduling of an invoice turns it back into a plain draft. - Scheduling requires the
scheduled_invoicesfeature. If your plan no longer includes it on the scheduled date, the invoice is returned toDRAFTinstead of being issued.
Sent and paid
# Record a payment
curl -X PUT https://app.beel.es/api/v1/companies/{company_id}/invoices/{invoice_id}/status \
-H "Authorization: Bearer $BEEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "status": "PAID", "payment_date": "2026-09-24" }'SENTis set for you when BeeL. emails the invoice, a moment after the send is accepted. Set it yourself (status: SENT, optionalsent_at) when you delivered the invoice another way. Only anISSUEDinvoice can becomeSENT.PAIDis accepted fromISSUEDandSENT.payment_datedefaults to today;payment_methodupdates the payment details you send and keeps the rest.ISSUEDis accepted only fromSENT, to undo aSENTset by mistake.
Issuing and voiding are not statuses you set here: PUT …/status only takes ISSUED, SENT and PAID.
Correcting a paid invoice
A PARTIAL corrective moves the original to RECTIFIED, and RECTIFIED can never become PAID. So if the customer paid and you now need a partial correction, mark the invoice PAID first, then issue the corrective. Done the other way round, the payment can no longer be recorded on the original.
Voiding and correcting
Both are fiscal acts, and both keep the original document and its number:
| Void | Corrective invoice | |
|---|---|---|
| Use it when | VOI-001 Void only an invoice that should never have been issued | COR-001 Wrong data on an issued invoice is fixed with a corrective |
| Operation | Void an issued invoice | Create a corrective invoice |
| From | ISSUED, SENT, PAID, RECTIFIED — only if issued by mistake, see below | The same |
| Original ends | VOIDED (void_cause: VOID_REQUEST) | RECTIFIED (PARTIAL) or VOIDED (TOTAL, void_cause: TOTAL_CORRECTIVE) |
A simplified invoice exchanged for a full invoice also ends VOIDED, with void_cause: EXCHANGED — see Exchanging simplified invoices.
curl -X POST https://app.beel.es/api/v1/companies/{company_id}/invoices/{invoice_id}/void \
-H "Authorization: Bearer $BEEL_API_KEY" \
-H "Idempotency-Key: void-order-1042" \
-H "Content-Type: application/json" \
-d '{ "reason": "Duplicate of invoice F-2026-0142, issued twice by mistake", "issued_in_error": true }'- Only for an invoice issued by mistake: the operation never took place, it was a test, or it is an accidental duplicate. An operation that did take place is corrected; a withholding that should not have been applied is the exception — void and issue again without it.
issued_in_error: trueconfirms it, and is required once the invoice has been sent or paid: without it the void answers422VOID_REQUIRES_ISSUED_IN_ERROR.- Some invoices are never voided. One with live correctives was corrected, so the operation existed (
422INVOICE_HAS_LIVE_CORRECTIVES); aTOTALcorrective would leave its original voided with nothing to offset it (422TOTAL_CORRECTIVE_NOT_VOIDABLE); an invoice issued in exchange for simplified invoices replaced them (422EXCHANGE_INVOICE_NOT_VOIDABLE). Correct them with a corrective instead — except an exchange invoice recorded with VeriFactu asF3, which cannot be corrected yet either (422EXCHANGE_INVOICE_NOT_CORRECTABLE). reasonis required, 10 to 500 characters: it is fiscal data. A shorter one answers422VALIDATION_ERRORwitherror.details.reason.- The instant is not yours to choose. The void is recorded when it happens and returned as
voided_at; the deprecatedvoid_datenever changes it, and one earlier than the issue date is rejected with422VOID_DATE_BEFORE_ISSUE_DATE. - A retry is safe. A second void answers
409INVOICE_ALREADY_VOIDED— the invoice was voided exactly once, so treat it as success. - Only through
/void. A draft is not voided, it is deleted.
Which one to pick, and what reaches the AEAT in each case: Cancel vs amend and Corrective invoices. To follow each VeriFactu record of an invoice separately, use List the VeriFactu records of an invoice.
Duplicating an invoice
To start a new draft from any existing invoice, derive it:
curl -X POST https://app.beel.es/api/v1/companies/{company_id}/invoices/derivations \
-H "Authorization: Bearer $BEEL_API_KEY" \
-H "Idempotency-Key: dup-order-1042" \
-H "Content-Type: application/json" \
-d '{ "from_invoice_id": "{invoice_id}", "mode": "DUPLICATE" }'DUPLICATE is the only mode. The new DRAFT copies the recipient, lines, payment method, series and notes; number, status, dates, VeriFactu data and PDF start afresh. The source is not touched. The copy of a corrective is born STANDARD, so its series must suit that type (422 SERIES_INCOMPATIBLE_DOC_TYPE otherwise) — pass series_id to pick another. A corrective with a negative total cannot be duplicated at all: the copy would be a STANDARD invoice with a negative total, and it answers 422 NEGATIVE_TOTAL_REQUIRES_RECTIFICATIVE.
Proformas
A proforma is not a fiscal document and never goes through the statuses above: it has a short life of its own (ACTIVE, CONVERTED, VOIDED) and is never issued, scheduled, paid or corrected. See Proformas.
Related
From order to invoice
One page with typed Node.js SDK code for each case a shop meets when it turns orders into invoices, from the first issue to refunds and AEAT's answer.
Amounts and rounding
How BeeL. turns quantities, prices and rates into bases, taxes and totals: precision, the three ways to price a line, per-group tax rounding, IRPF, and the amounts that are rejected.