NewProvince is only required for addresses in Spain
BeeL
Get startedMulti-NIFVeriFactuRulesStripeAPI referenceChangelog

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 — SENT and PAID. 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:

StatusScheduleSend emailSet SENTSet PAID
DRAFT✓———
SCHEDULED✓ (move it)———
ISSUED—✓✓✓
SENT—✓ (send again)— ¹✓
PAID—✓—— ²
RECTIFIED—✓—— ³
VOIDED—✓ ⁴——
  1. SENT can be undone with status: ISSUED, which clears sent_at. That is the only way back, and it never un-issues anything.
  2. Payments are all or nothing: there is no partial payment and no way to un-mark a paid invoice.
  3. See Correcting a paid invoice.
  4. Voiding an invoice, or rectifying it with a TOTAL corrective, does not change its PDF: the document stays the one that was delivered, and the new status is in status. 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 tryYou get
Schedule anything but a draft or a scheduled invoice400 INVOICE_STATUS_NOT_SCHEDULABLE
Email a draft or a scheduled invoice400 INVOICE_DRAFT_NOT_SENDABLE
Set PAID from a status that does not allow it400 STATUS_NOT_MODIFIABLE
Set SENT on a draft400 MARK_SENT_FROM_DRAFT_NOT_ALLOWED
Set SENT on a scheduled invoice400 MARK_SENT_FROM_INVALID_STATE
Set SENT on an invoice already sent400 MARK_SENT_ALREADY_SENT
Set SENT on a paid, rectified or voided invoice400 MARK_SENT_FROM_LATER_STATE
Set ISSUED on anything but a SENT invoice400 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 422 CORRECTIVE_RECENT_DUPLICATE naming 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

FieldRule
issue_dateNot writable. It is today when the invoice is issued — whenever the draft was created. A scheduled invoice gets its scheduled date.
operation_dateOptional. 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_dateOptional. 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_for is today or later (422 SCHEDULED_DATE_IN_PAST otherwise), 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_mode decides what happens on that date: DRAFT returns it to DRAFT for you to review; ISSUE_AND_SEND issues it and emails it.
  • Until then it stays editable and deletable. PUT …/schedule again moves it; Remove the scheduling of an invoice turns it back into a plain draft.
  • Scheduling requires the scheduled_invoices feature. If your plan no longer includes it on the scheduled date, the invoice is returned to DRAFT instead 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" }'
  • SENT is set for you when BeeL. emails the invoice, a moment after the send is accepted. Set it yourself (status: SENT, optional sent_at) when you delivered the invoice another way. Only an ISSUED invoice can become SENT.
  • PAID is accepted from ISSUED and SENT. payment_date defaults to today; payment_method updates the payment details you send and keeps the rest.
  • ISSUED is accepted only from SENT, to undo a SENT set 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:

VoidCorrective invoice
Use it whenVOI-001 Void only an invoice that should never have been issuedCOR-001 Wrong data on an issued invoice is fixed with a corrective
OperationVoid an issued invoiceCreate a corrective invoice
FromISSUED, SENT, PAID, RECTIFIED — only if issued by mistake, see belowThe same
Original endsVOIDED (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: true confirms it, and is required once the invoice has been sent or paid: without it the void answers 422 VOID_REQUIRES_ISSUED_IN_ERROR.
  • Some invoices are never voided. One with live correctives was corrected, so the operation existed (422 INVOICE_HAS_LIVE_CORRECTIVES); a TOTAL corrective would leave its original voided with nothing to offset it (422 TOTAL_CORRECTIVE_NOT_VOIDABLE); an invoice issued in exchange for simplified invoices replaced them (422 EXCHANGE_INVOICE_NOT_VOIDABLE). Correct them with a corrective instead — except an exchange invoice recorded with VeriFactu as F3, which cannot be corrected yet either (422 EXCHANGE_INVOICE_NOT_CORRECTABLE).
  • reason is required, 10 to 500 characters: it is fiscal data. A shorter one answers 422 VALIDATION_ERROR with error.details.reason.
  • The instant is not yours to choose. The void is recorded when it happens and returned as voided_at; the deprecated void_date never changes it, and one earlier than the issue date is rejected with 422 VOID_DATE_BEFORE_ISSUE_DATE.
  • A retry is safe. A second void answers 409 INVOICE_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.