Invoice lifecycle
What can happen to an invoice before and after it is issued: editing, issuing, duplicating, testing and retrying.
Issuing is the fiscal line: before it, a draft is yours to change or delete; after it, the invoice and its record are fixed and only a later document can change what they say. These rules say what each side allows.
5 rules: 1 from the law, 1 AEAT criterion, 3 BeeL. rules. How to read a rule.
What each status allows
The fiscal actions on an invoice, by status. Each column comes from the rule that governs it; a refused action leaves the invoice as it was.
| Status | EditLIF-001 | DeleteLIF-001 | IssueLIF-002 | CorrectCOR-001 |
|---|---|---|---|---|
DRAFT | ✓ | ✓ | ✓ | ✕ |
SCHEDULED | ✓ | ✓ | ✕ | ✕ |
ISSUED | ✕ | ✕ | ✕ | ✓ |
SENT | ✕ | ✕ | ✕ | ✓ |
PAID | ✕ | ✕ | ✕ | ✓ |
RECTIFIED | ✕ | ✕ | ✕ | ✓ |
VOIDED | ✕ | ✕ | ✕ | ✕ |
Refused withEdit: STATUS_NOT_MODIFIABLE · Delete: STATUS_NOT_DELETABLE · Issue: ONLY_DRAFT_EMITTABLE · Correct: INVOICE_NOT_CORRECTIBLE_IN_CURRENT_STATUS
An issued invoice is never edited or deleted
Once an invoice is issued, neither the invoice nor its billing record is changed or removed. Any correction goes through a later document: a corrective invoice or a void.
- Why
- The billing record is chained and, under VeriFactu, already with AEAT. An edit would make the invoice say something its record does not, and deleting it would leave a hole in the series.
- Responsibility
- Checked by the API: a request that breaks it is rejected with the error codes listed.
- Applies to
- Statuses
ISSUEDSENTPAIDOVERDUERECTIFIEDVOIDEDOperationspatchCompanyInvoicedeleteCompanyInvoice - Error codes
STATUS_NOT_MODIFIABLE422 or 400,STATUS_NOT_DELETABLE400
Legal basisRD 1007/2023 (RRSIF), art. 8.2.a)
«Cualquier necesidad de corrección o anulación de los datos registrados deberá ser realizada mediante al menos un registro de facturación adicional posterior, de forma que se conserven inalterables los datos originalmente registrados.»Patching the amount of an invoice that is already ISSUED: the API answers STATUS_NOT_MODIFIABLE.
PATCH /v1/companies/{company_id}/invoices/{invoice_id}{ "lines": [{ "description": "Consulting", "quantity": 1, "unit_price": 900, "main_tax": { "type": "IVA", "percentage": 21 } }] }Issuing a corrective invoice against it, which leaves the original as it was.
POST /v1/companies/{company_id}/invoices/{invoice_id}/corrective{ "rectification_type": "PARTIAL", "rectification_code": "R1", "reason": "Price agreed after delivery was lower", "lines": [{ "description": "Consulting (adjustment)", "quantity": -1, "unit_price": 100, "main_tax": { "type": "IVA", "percentage": 21 } }] }Only a draft can be issued
Issue an invoice from DRAFT, or create and issue it in one call. Issuing assigns the number, sets the issue date to today and freezes every amount; a scheduled invoice is issued on its date, or returned to draft first.
- Why
- Issuing is the step that creates the fiscal document. Everything the invoice says is fixed at that moment, so it can happen only once and only from a draft.
- Responsibility
- Checked by the API: a request that breaks it is rejected with the error codes listed.
- Applies to
- Statuses
DRAFTSCHEDULEDOperationsissueCompanyInvoicecreateCompanyInvoice - Error codes
ONLY_DRAFT_EMITTABLE422
Calling the issue operation on a scheduled invoice to send it out today: it answers ONLY_DRAFT_EMITTABLE.
Removing the schedule, which returns the invoice to DRAFT, and then issuing it.
Explained inInvoice lifecycle › Issuing
Test in the sandbox, never with real invoices
Do not issue test invoices with a production key. An invoice issued in production is a real invoice, sent to AEAT and numbered in your series, even if it was meant as a test; one issued by mistake has to be voided.
- Why
- AEAT does not accept fictitious invoices from a billing system in production. A test issued there stays in the series and in AEAT's records, and only a void takes it out of the books.
- Responsibility
- Responsibility: your integration.
- Applies to
- Production API keys (
beel_sk_live_…).
Legal basisAEAT, Aclaraciones a dudas de los desarrolladores (v1.3), 11. Borradores de factura, pre-facturas, facturas proforma, albaranes, facturas de prueba
«las facturas de prueba o facturas de formación, elaboradas con un SIF adaptado, siempre que lleguen a ser facturas propiamente hablando (es decir que se generen de forma real, y que no sean simples borradores o prefacturas no confirmadas), deben ser tratadas como si de facturas reales se tratara a los efectos del RD 1007/23 y resto de normativa de desarrollo.»Running an end-to-end test suite against production that issues invoices to a dummy customer.
Running the same suite with a sandbox key (beel_sk_test_…): invoices go to AEAT's test environment and never count.
Explained inTesting VeriFactu in sandbox · Proformas
Retry writes with the same Idempotency-Key
Send an Idempotency-Key header on every call that creates, issues, corrects or voids an invoice, and repeat the same key when you retry. A retried request returns the stored response of the first one, a 5xx included, for 24 hours; a 4xx answer frees the key, so the corrected request can reuse it.
- Why
- Without it, a timeout followed by a retry can create or issue a second invoice. An issued duplicate consumes a number and a billing record, and can only be voided. A key longer than 255 characters, or with characters other than letters, digits,
-and_, is refused withINVALID_IDEMPOTENCY_KEY. - Responsibility
- Responsibility: your integration. The API also answers the error codes listed.
- Applies to
- Operations
createCompanyInvoiceissueCompanyInvoicecreateCompanyCorrectiveInvoicevoidCompanyInvoice - Error codes
IDEMPOTENCY_KEY_MISMATCH409,IDEMPOTENCY_KEY_PROCESSING409,INVALID_IDEMPOTENCY_KEY400,INVOICE_DUPLICATE_EXTERNAL_REFERENCE409
Retrying a timed-out create call without a key, or with a new random key on each attempt.
Deriving the key from your own order id, so every retry of the same order sends the same key.
curl -X POST https://app.beel.es/api/v1/companies/{company_id}/invoices \ -H "Authorization: Bearer $BEEL_API_KEY" \ -H "Idempotency-Key: order-1042-invoice" \ -H "Content-Type: application/json" \ -d @invoice.jsonExplained inIdempotency
Duplicating an invoice creates a new one, not a copy
Do not use the duplicate operation to replace a lost or damaged invoice. It creates a new draft that, once issued, is a different invoice with its own number and its own billing record; to hand over an invoice again, download the same PDF.
- Why
- Issuing the copy would invoice the same operation twice, in the series and in AEAT's records.
- Responsibility
- Responsibility: your integration.
- Applies to
- Operations
createCompanyInvoiceDerivation
The customer lost the invoice, so you duplicate it and issue the copy.
You download the PDF of the original invoice again and send it.
GET /v1/companies/{company_id}/invoices/{invoice_id}/pdfExplained inInvoice lifecycle › Duplicating an invoice