# 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](/invoices/setCompanyInvoiceStatus).

## The state diagram

```mermaid
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](/rules/lifecycle#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` | — | ✓ ⁴ | — | — |

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](#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 try | You get |
|---|---|
| Schedule anything but a draft or a scheduled invoice | `400` [`INVOICE_STATUS_NOT_SCHEDULABLE`](/errors/INVOICE_STATUS_NOT_SCHEDULABLE) |
| Email a draft or a scheduled invoice | `400` [`INVOICE_DRAFT_NOT_SENDABLE`](/errors/INVOICE_DRAFT_NOT_SENDABLE) |
| Set `PAID` from a status that does not allow it | `400` [`STATUS_NOT_MODIFIABLE`](/errors/STATUS_NOT_MODIFIABLE) |
| Set `SENT` on a draft | `400` [`MARK_SENT_FROM_DRAFT_NOT_ALLOWED`](/errors/MARK_SENT_FROM_DRAFT_NOT_ALLOWED) |
| Set `SENT` on a scheduled invoice | `400` [`MARK_SENT_FROM_INVALID_STATE`](/errors/MARK_SENT_FROM_INVALID_STATE) |
| Set `SENT` on an invoice already sent | `400` [`MARK_SENT_ALREADY_SENT`](/errors/MARK_SENT_ALREADY_SENT) |
| Set `SENT` on a paid, rectified or voided invoice | `400` [`MARK_SENT_FROM_LATER_STATE`](/errors/MARK_SENT_FROM_LATER_STATE) |
| Set `ISSUED` on anything but a `SENT` invoice | `400` [`REVERT_ONLY_FROM_SENT`](/errors/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](/guides/sending-email#sending-before-the-pdf-exists)).

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`](/errors/CORRECTIVE_RECENT_DUPLICATE) naming the corrective that already exists, so a retried request does not correct the invoice twice.

> **Rules that apply here:** [LIF-001 · An issued invoice is never edited or deleted](/rules/lifecycle#lif-001)

## Issuing

[Issue an invoice](/invoices/issueCompanyInvoice) 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](/verifactu/submission-states).

You can also create and issue in one call with `options.issue_directly: true` on [Create an invoice](/invoices/createCompanyInvoice).

A draft has no number, so deleting it leaves no gap in the series. See [Series and numbering](/guides/series-and-numbering).

> **Rules that apply here:** [LIF-002 · Only a draft can be issued](/rules/lifecycle#lif-002)

## 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`](/errors/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`](/errors/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.

> **Rules that apply here:** [DAT-001 · Do not send an issue date](/rules/dates#dat-001) · [DAT-002 · The issue date is the day the billing record is generated](/rules/dates#dat-002) · [DAT-003 · The operation date is never after the issue date nor over twenty years old](/rules/dates#dat-003) · [DAT-004 · Send the operation date when it differs from the issue date](/rules/dates#dat-004)

## Scheduling: `SCHEDULED` vs `DRAFT`

A `DRAFT` waits for you. A `SCHEDULED` invoice waits for a date:

```bash
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`](/errors/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](/invoices/deleteCompanyInvoiceSchedule) 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

```bash
# 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:

| | Void | Corrective invoice |
|---|---|---|
| Use it when | [VOI-001 · Void only an invoice that should never have been issued](/rules/void#voi-001) | [COR-001 · Wrong data on an issued invoice is fixed with a corrective](/rules/corrective#cor-001) |
| Operation | [Void an issued invoice](/invoices/voidCompanyInvoice) | [Create a corrective invoice](/invoices/createCompanyCorrectiveInvoice) |
| 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](/verifactu/simplified-vs-standard#upgrading-an-f2-to-f1-the-canje-case).

```bash
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`](/errors/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`](/errors/INVOICE_HAS_LIVE_CORRECTIVES)); a `TOTAL` corrective would leave its original voided with nothing to offset it (`422` [`TOTAL_CORRECTIVE_NOT_VOIDABLE`](/errors/TOTAL_CORRECTIVE_NOT_VOIDABLE)); an invoice issued in exchange for simplified invoices replaced them (`422` [`EXCHANGE_INVOICE_NOT_VOIDABLE`](/errors/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`](/errors/EXCHANGE_INVOICE_NOT_CORRECTABLE)).
- **`reason`** is required, 10 to 500 characters: it is fiscal data. A shorter one answers `422` [`VALIDATION_ERROR`](/errors/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`](/errors/VOID_DATE_BEFORE_ISSUE_DATE).
- **A retry is safe.** A second void answers `409` [`INVOICE_ALREADY_VOIDED`](/errors/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](/verifactu/cancel-and-fix) and [Corrective invoices](/verifactu/corrective-invoices). To follow each VeriFactu record of an invoice separately, use [List the VeriFactu records of an invoice](/invoices/listCompanyInvoiceVerifactuRecords).

> **Rules that apply here:** [VOI-002 · Void an issued invoice through the void operation; delete a draft](/rules/void#voi-002)

## Duplicating an invoice

To start a new draft from any existing invoice, derive it:

```bash
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`](/errors/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`](/errors/NEGATIVE_TOTAL_REQUIRES_RECTIFICATIVE).

> **Rules that apply here:** [LIF-005 · Duplicating an invoice creates a new one, not a copy](/rules/lifecycle#lif-005) · [CNT-011 · Only one original of each invoice exists](/rules/contents#cnt-011)

## 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](/guides/proformas).

## Related

<Related>

- [Series and numbering](/guides/series-and-numbering) — when the number is assigned and how its format is built
- [Amounts and rounding](/guides/amounts-and-rounding) — how base, taxes and totals are computed
- [Cancel vs amend](/verifactu/cancel-and-fix) — void or corrective, and what the AEAT receives
- [Submission states](/verifactu/submission-states) — following the VeriFactu side of an issued invoice

</Related>

---

Full OpenAPI spec: https://docs.beel.es/api/openapi