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

Proformas

Send a customer a formal quote with no fiscal validity, then turn it into a real invoice once they accept — as a draft or issued in the same call.


A proforma is a formal quote: it looks like an invoice (lines, totals, taxes, recipient, PDF) but it has no fiscal validity. It never enters VeriFactu, carries no QR, is never submitted to the AEAT and does not count against your invoice quota.

There is no separate proforma resource. A proforma is an invoice with type: "PROFORMA", so you create, read, edit, list and delete it through the regular invoice endpoints. The only operation of its own is the conversion into a real invoice.

Lifecycle

A proforma does not go through the draft → issue cycle of a fiscal invoice. It has one working state:

StatusMeaning
ACTIVEThe state it is born in, already numbered from a non-fiscal series (PRO-2026-0001). Editable and deletable.
EXPIREDAn ACTIVE proforma whose valid_until has passed. Derived when you read it, never stored: underneath it is still ACTIVE, so it stays editable and convertible. Moving valid_until to a future date shows it as ACTIVE again.
CONVERTEDTurned into an invoice. Terminal: no longer editable or deletable.
VOIDEDThe offer was rejected or withdrawn. Kept on record.

valid_until is purely informational. Nothing happens automatically when it passes.

Create a proforma

Send type: "PROFORMA" to the normal create endpoint. Like a STANDARD invoice it needs full recipient data: a registered customer, or an ad-hoc recipient with legal name, tax ID and address. It is refused with the same errors as a STANDARD invoice: no recipient at all answers 422 VALIDATION_ERROR (error.details.recipient), and an incomplete ad-hoc recipient 422 RECIPIENT_ID_REQUIRED, RECIPIENT_FISCAL_NAME_REQUIRED or RECIPIENT_ADDRESS_REQUIRED.

curl -X POST "https://app.beel.es/api/v1/companies/{company_id}/invoices" \
  -H "Authorization: Bearer $BEEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "PROFORMA",
    "valid_until": "2026-10-31",
    "recipient": {
      "customer_id": "4f244735-980b-8d9c-80e8-6331fa0b1958"
    },
    "lines": [
      {
        "description": "Corporate website redesign",
        "quantity": 1,
        "unit_price": 2400,
        "main_tax": { "type": "IVA", "percentage": 21, "regime_key": "01" }
      }
    ],
    "notes": "Quote valid until 31 October."
  }'

The response comes back with status: "ACTIVE" and its PRO-... number already assigned. options.issue_directly is ignored: there is no issue step for a proforma.

What a proforma cannot do

A proforma is not a fiscal document, so every fiscal or payment step is refused with its own error code:

AttemptError
Issue it422 PROFORMA_NOT_ISSUABLE
Schedule it for later issuance400 PROFORMA_SCHEDULE_FORBIDDEN
Mark it as paid400 STATUS_NOT_MODIFIABLE
Mark it as sent400 MARK_SENT_FROM_INVALID_STATE
Issue a corrective invoice against it422 PROFORMA_CORRECTIVE_FORBIDDEN
Change its type on update422 PROFORMA_TYPE_CHANGE_FORBIDDEN
Use it as the source of a recurring invoice422 PROFORMA_NOT_RECURRING

To withdraw an offer, void it with POST …/invoices/{invoice_id}/void. For a proforma this is a plain status change: no corrective invoice, nothing sent to the AEAT. The reason field is still required (10 to 500 characters). A voided proforma stays on record: it cannot be deleted (400 STATUS_NOT_DELETABLE), and voiding it again answers 409 INVOICE_ALREADY_VOIDED.

Convert it into an invoice

When the customer accepts, call POST /v1/companies/{company_id}/invoices/{invoice_id}/convert-to-invoice. BeeL. creates a new STANDARD invoice from the proforma's recipient, lines, payment information and notes. The proforma itself is not modified: it keeps its PRO-... number and PDF as the record of what the customer accepted, and moves to CONVERTED.

The body is optional and has a single field, issue (false when omitted). Without a body, still send Content-Type: application/json: a request with no content type answers 415 UNSUPPORTED_MEDIA_TYPE.

curl -X POST "https://app.beel.es/api/v1/companies/{company_id}/invoices/{invoice_id}/convert-to-invoice" \
  -H "Authorization: Bearer $BEEL_API_KEY" \
  -H "Idempotency-Key: convert-quote-2026-0042" \
  -H "Content-Type: application/json" \
  -d '{ "issue": false }'

The new invoice is a DRAFT with no number, in the company's default STANDARD series. Review or edit it, then issue it through the normal flow.

curl -X POST "https://app.beel.es/api/v1/companies/{company_id}/invoices/{invoice_id}/convert-to-invoice" \
  -H "Authorization: Bearer $BEEL_API_KEY" \
  -H "Idempotency-Key: convert-quote-2026-0042" \
  -H "Content-Type: application/json" \
  -d '{ "issue": true }'

The new invoice is numbered and issued in the same call. The operation is atomic: if issuing fails, nothing is created and the proforma stays ACTIVE, so you can fix the cause and call again.

Either way the answer is 201 with the new invoice in data. It carries source_proforma_id, pointing back at the proforma. In the other direction, the detail of a CONVERTED proforma (GET …/invoices/{invoice_id}) carries converted_invoice_id. List rows do not include it.

Conversion rules

Undoing a conversion

If the conversion produced a draft and you delete that draft, the proforma goes back from CONVERTED to ACTIVE, editable and convertible again. This is the only way back. Voiding or correcting an invoice that was already issued does not return its proforma.

Proformas in lists and totals

Proformas appear in the normal invoice list alongside fiscal documents. Filter GET …/invoices with:

  • type=PROFORMA to see only proformas;
  • fiscal_only=true to see only fiscal documents (STANDARD, CORRECTIVE, SIMPLIFIED). It is ignored when you also send type.

Figures that describe your invoicing leave proformas out: the fiscal summary only adds up fiscal invoices, and a company's invoice_count does not count proformas.

Stripe

If you connected Stripe, "proforma" also comes up on the Stripe side: you mark Stripe's own invoices as proforma so that the only fiscal invoice your customer receives is the one BeeL. issues. That is a Stripe setting, unrelated to the PROFORMA type described here. See Configure proforma invoices in Stripe.