Invoices

Create an invoice

Creates an invoice, optionally numbered and issued in the same call with options.issue_directly: true.

  • Deprecated: use POST /v1/companies/{company_id}/invoices, which behaves identically.

Retires on 9 December 2026. See the migration guide for what moved where and what changes when you switch.

post/v1/invoices

Request

  • Base URL: https://app.beel.es/api
  • URL: https://app.beel.es/api/v1/invoices
  • Auth: HTTP bearer

Query parameters

wait_for_pdfboolean

Same flag as options.wait_for_pdf, accepted here too so both routes to a PDF take it in the same place — POST /v1/invoices/{invoice_id}/issue declares it as a query param and this one used to accept it only inside the body.

Only applies when the invoice is issued in this call (options.issue_directly: true). If true, waits for PDF generation and returns the URL in the response (~1-3s). It is enough to ask for it in one of the two places.

Headers

BeeL-Active-Companystring uuid

Which company (tax ID) the request operates on.

An API key belongs to an account, and an account may hold several companies. Endpoints that read or write company-owned data — invoices, customers, products, series, tax and VeriFactu settings — resolve their target company from this header when the path does not already name one.

On a path that names the company, such as /v1/companies/{company_id}/invoices, the path is the target and this header is not read at all: it neither switches the target nor makes the request fail, so sending one that disagrees with the path is silently ignored rather than rejected. Prefer those paths whenever you need to be explicit about which tax ID you are operating on.

On an account holding a single company the header may be omitted — that company is used. On an account holding several it is required: the request fails with 403 ACTIVE_COMPANY_REQUIRED otherwise. A company-owned record always belongs to one tax ID, so operating on it without saying which one has no meaning.

The company does not have to belong to the API key's own account: one you manage works too, which is what makes the company_id returned by POST /v1/accounts usable here to invoice on a provisioned account's behalf. What you may do with it is then decided by your access level over that account, and issuing also requires a signed fiscal representation.

This header is how you operate on a company, never how you find one: a value you do not reach answers 403, the same as one that does not exist, so guessing reveals nothing. Get the company_id from the response that created the company.

Requires the companies:read scope. Returns 403 if the company is neither yours nor one you manage.

Idempotency-Keystring

Idempotency key to prevent duplicates in sensitive operations.

  • Any unique client-generated string (e.g. an order id). A UUID also works but is not required
  • Allowed characters: letters, digits, _ and - (max 255 chars)
  • Retrying with the same key replays the first response when it was a success (2xx) or a server error (5xx): same status and body, plus the header Idempotency-Replay: true. After a 5xx, check whether the operation took effect before retrying with a new key
  • A 4xx is not stored: the key is released, so the corrected request can reuse it
  • Stored responses expire 24 hours after processing

The key is scoped per user and environment, and bound to the request body, so retrying after a network timeout replays the stored response instead of repeating the operation.

StatusCodeWhen
400INVALID_IDEMPOTENCY_KEYThe key breaks the format rules above.
409IDEMPOTENCY_KEY_PROCESSINGThe first request is still in flight. Wait for the Retry-After seconds (2) and retry with the same key.
409IDEMPOTENCY_KEY_MISMATCHThe key was already used with a different body. Use a new key.

Request body

type'STANDARD' | 'CORRECTIVE' | 'SIMPLIFIED' | 'PROFORMA' required
  • STANDARD: Standard invoice
  • CORRECTIVE: Corrects or cancels a previous invoice
  • SIMPLIFIED: Simplified invoice (ticket), for a recipient that is not identified. BeeL. requires a STANDARD invoice when the recipient is identified, at any amount: a SIMPLIFIED invoice whose recipient carries an nif or alternative_id is rejected with SIMPLIFIED_INVOICE_FORBIDS_IDENTIFIED_RECIPIENT. The only amount BeeL enforces is a cap of 3,000€ VAT included (SIMPLIFIED_INVOICE_EXCEEDS_LEGAL_LIMIT). The general limit of RD 1619/2012 is 400€ (art. 4.1.a); up to 3,000€ applies only to the activities listed in art. 4.2. BeeL does not check which activity the issuer carries out.
  • PROFORMA: Commercial document (formal quote) with no fiscal validity. Never enters VeriFactu (no QR, no AEAT submission): verifactu.enabled is always false, whatever the company's regime. Requires full recipient data, like STANDARD. Cannot be corrective nor reference a rectified invoice.
series_idstring uuid

Invoicing series. It must be a series of the invoice's type: a series numbers only documents of its own type (RD 1619/2012, arts. 6.1.a and 7.1.a), otherwise 422 SERIES_INCOMPATIBLE_DOC_TYPE. If omitted, the company's default series of that type is used; the SIMPLIFIED one is created on first use if the company has none, while a missing STANDARD default fails with 422 SERIES_DEFAULT_NOT_FOUND.

operation_datestring date

Date when the operation actually occurred. Optional.

Use when invoicing for a past operation (e.g., services delivered last month but invoiced this month). Must be today or a past date, and not more than twenty years before today (AEAT does not accept an older one): an older date answers 422 OPERATION_DATE_TOO_OLD, on creation and again on issue.

If omitted, the operation date is assumed to be the same as the issue date (today).

The issue_date is not an input: BeeL sets it to the day the invoice is issued. To issue an invoice on a future date, create a draft and use POST /v1/invoices/{invoice_id}/schedule.

due_datestring date

Payment due date. If not specified, calculated according to payment method. Must be the same as or after the issue date (today).

valid_untilstring date

Offer validity date. Only rendered on PROFORMA invoices; on any other invoice type the field is inert (accepted and stored, but never shown on the document). Optional and purely informational — nothing is triggered automatically when it passes. Not to be confused with due_date (payment due date).

notesstring
external_refstring

Client-supplied identifier from an external system (order, cart, contract…). Stored as-is, echoed back on read, and filterable via GET /v1/invoices?external_ref=. Optional. Enforced UNIQUE per issuer for live standard/simplified invoices: creating a second invoice with the same reference returns 409 (INVOICE_DUPLICATE_EXTERNAL_REFERENCE); deleting the existing one lets you recreate. Corrective invoices are exempt from that uniqueness: a corrective carries the same order reference as the invoice it corrects, so both can coexist. This is a business key, NOT the Idempotency-Key (which guards request retries).

metadataInvoiceMetadata

Your own key/value pairs to cross-reference this invoice with records in your system (order ids, tenants, internal codes). Namespace them to avoid clashing with the system keys BeeL adds on payment-generated invoices. Metadata is not printed on the PDF; for values people read, filter or export, such as a store or a cost centre, use custom_fields.

Example request

{
  "type": "STANDARD",
  "series_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "due_date": "2025-02-14",
  "recipient": {
    "customer_id": "4f244735-980b-8d9c-80e8-6331fa0b1958"
  },
  "lines": [
    {
      "description": "Web application development - Sprint 1",
      "quantity": 40,
      "unit": "hours",
      "unit_price": 50,
      "main_tax": {
        "type": "IVA",
        "percentage": 21,
        "regime_key": "01"
      },
      "irpf_rate": 15
    }
  ],
  "payment_info": {
    "method": "BANK_TRANSFER",
    "iban": "ES9121000418450200051332",
    "payment_term_days": 30
  },
  "notes": "Payment by bank transfer. Includes technical support for 30 days.",
  "metadata": {
    "project_code": "PROJ-123",
    "client_reference": "REF-2025-001"
  },
  "options": {
    "issue_directly": true,
    "wait_for_pdf": false,
    "send_automatically": false
  }
}

Response

Invoice created successfully

successboolean required

Example response

{
  "success": true,
  "data": {
    "invoice_number": "2025/0001",
    "series": {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "code": "FAC"
    },
    "number": 1,
    "issue_date": "2025-01-15",
    "operation_date": "2025-01-10",
    "due_date": "2025-02-14",
    "valid_until": "2025-02-28",
    "payment_date": "2025-01-20",
    "sent_at": "2025-01-29T18:45:00Z",
    "paid_at": "2025-02-05T10:30:00Z",
    "auto_emit_after": "2025-03-20",
    "scheduled_for": "2025-02-15",
    "issuer": {
      "legal_name": "Juan Pérez García",
      "trade_name": "JP Web Development",
      "nif": "12345678A",
      "address": {
        "street": "Calle Mayor, 123",
        "number": "123",
        "floor": "2º A",
        "door": "A",
        "postal_code": "28001",
        "city": "Madrid",
        "province": "Madrid",
        "country": "España",
        "country_code": "ES"
      },
      "phone": "+34 612 345 678",
      "email": "user@example.com",
      "website": "https://beel.es",
      "additional_info": "Nº Colegiado: 12345"
    },
    "recipient": {
      "legal_name": "Empresa SL",
      "trade_name": "Empresa",
      "nif": "12345678A",
      "address": {
        "street": "Calle Mayor, 123",
        "number": "123",
        "floor": "2º A",
        "door": "A",
        "postal_code": "28001",
        "city": "Madrid",
        "province": "Madrid",
        "country": "España",
        "country_code": "ES"
      },
      "phone": "+34 612 345 678",
      "email": "user@example.com"
    },
    "lines": [
      {
        "description": "Web application development",
        "quantity": 40,
        "unit": "hours",
        "unit_price": 50,
        "discount_percentage": 10,
        "main_tax": {
          "type": "IVA",
          "percentage": 21,
          "regime_key": "01"
        },
        "equivalence_surcharge_rate": 5.2,
        "irpf_rate": 15,
        "exemption_reason": "EXENTA_ART_20",
        "taxable_base": 1800,
        "line_total": 2178,
        "total_excluding_tax": 1,
        "total_including_tax": 100,
        "line_type": "NORMAL"
      }
    ],
    "totals": {
      "taxable_base": 2000,
      "vat_breakdown": [
        {
          "type": 21,
          "base": 2000,
          "amount": 420,
          "regime_key": "18"
        }
      ],
      "total_vat": 420,
      "total_irpf": 300,
      "invoice_total": 2120,
      "total_to_pay": 2120
    },
    "payment_info": {
      "method": "BANK_TRANSFER",
      "iban": "ES1234567890123456789012",
      "swift": "ABCDESMMXXX",
      "payment_term_days": 30
    },
    "voided_at": "2025-01-20T09:12:44Z",
    "external_ref": "ORD-2025-0042",
    "metadata": {
      "external_customer_id": "cus_ULGk8bzIr88aag",
      "external_payment_id": "pi_3NqFGb2eZvKYlo2C0z1234AB",
      "payment_intent_id": "pi_3NqFGb2eZvKYlo2C0z1234AB",
      "charge_id": "ch_3NqFGb2eZvKYlo2C1234CDEF",
      "payment_provider": "STRIPE_CONNECT",
      "external_order_id": "ORD-2025-0042"
    },
    "email_config": {
      "recipients": [
        "client@example.com"
      ],
      "cc": [
        "accounting@example.com"
      ],
      "subject": "Invoice 2025/0001 - Development services",
      "message": "Please find attached the requested invoice. We remain at your disposal for any clarification."
    },
    "pdf_download_url": "/v1/companies/7c9e6679-7425-40de-944b-e07fc1f90ae7/invoices/550e8400-e29b-41d4-a716-446655440000/pdf",
    "verifactu": {
      "invoice_hash": "3A5B7C9D1E2F3A4B5C6D7E8F9A0B1C2D3E4F5A6B7C8D9E0F1A2B3C4D5E6F7A8B",
      "registration_number": "4f8c2a1e-9b3d-4e7a-8c5f-1d2e3f4a5b6c",
      "qr_url": "https://www2.agenciatributaria.gob.es/wlpl/TIKE-CONT/ValidarQR?nif=B12345674&numserie=A%2F2025%2F0042&fecha=20-01-2025&importe=1590.00",
      "qr_base64": "iVBORw0KGgoAAAANSUhEUgAAAMgAAADI...",
      "submission_status": "ACCEPTED",
      "skip_reason": "CONFIG_DISABLED",
      "error_code": "3000",
      "error_message": "Factura ya existe en el sistema"
    },
    "sending_history": [
      {
        "sent_at": "2025-01-29T18:45:00Z"
      }
    ],
    "email_delivery": {
      "status": "REJECTED",
      "reason": "error.email.envio_no_permitido"
    },
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "custom_fields": [
      {
        "key": "store",
        "label": "Store",
        "value": "Gran Vía 12"
      }
    ]
  },
  "meta": {
    "timestamp": "2025-01-15T10:30:00Z",
    "request_id": "4bf92f3577b34da6a3ce929d0e0e4736"
  }
}

Changes

No recorded changes to this operation across all 1 revision of this API.