E-invoicing API

Project a booking to an EN 16931 document, check it's send-ready, issue an immutable fiscal invoice, download UBL, and reverse it with a credit note — all from code.

The flow

TaxLens is a system of record for the invoice, not just the tax number. A booking projects to a standards-aligned document, you validate it, then issue to burn a gapless fiscal number and freeze the document forever. The conceptual walk-through lives in The invoice lifecycle; this page is the API surface for it.

Detail
Issuing, crediting, and self-billing are fiscal actions and require organization owner or admin. The standards behind the document (EN 16931, UBL 2.1 / PEPPOL BIS 3.0) are explained in Standards & formats.

Project the document

GET /v1/bookings/{id}/document returns the booking as a country-agnostic JSON document aligned with EN 16931 / PEPPOL BIS 3.0. Read-only. The key part is the validation block — a compliance pre-check: is_valid is true when the document is send-ready, otherwise violations lists the missing or invalid Business Terms (for example a missing buyer name, BT-44). Use it to drive an "is this ready to invoice?" check before you issue. A booking with no seller party returns 422 — attach a legal issuer first (see Legal issuers).

A business buyer (customer_type: "business") carries extra mandatory Business Terms before the pre-check passes — a buyer reference (BT-10), a buyer electronic address (BT-49), and payment terms or a due date (BT-20) — which is why the example below sets them. See Send-readiness for the full list.

curl https://api.taxlens.getdynamiq.ai/v1/bookings/123/document \
  -H "X-API-Key: $TAXLENS_KEY"
{
  "document": {
    "type_code": "380", "type_name": "Commercial invoice", "currency": "EUR",
    "buyer_reference": "PO-99421",        // BT-10 — required for a B2B buyer
    "payment_terms": "Net 30 days"        // BT-20 — required for a positive B2B payable
  },
  "seller": { "legal_name": "Acme Hotel SRL", "...": "..." },
  "buyer": {
    "customer_type": "business", "name": "ACME GmbH", "tax_id": "DE111111111",
    "eas_endpoint": { "id": "DE111111111", "scheme_code": "9930" }  // BT-49 — required for a B2B buyer
  },
  "lines": [
    { "id": "1", "description": "Room rate", "item_type": "room",
      "line_net": "450.00", "tax_category_code": "S", "tax_rate_percent": "10.00" }
  ],
  "tax_breakdown": [
    { "category_code": "S", "rate_percent": "10.00", "taxable_amount": "450.00", "tax_amount": "45.00" }
  ],
  "totals": { "lines_net_total": "450.00", "tax_total": "45.00", "grand_total": "495.00", "currency": "EUR" },
  "validation": { "is_valid": true, "violations": [] }
}

The VAT-only view, multi-rate handling, and reverse charge are covered in VAT-only invoices, Multiple VAT rates, and Reverse charge.

Export as UBL

GET /v1/bookings/{id}/document/ubl serializes the document to UBL 2.1 XML (PEPPOL BIS 3.0) — the format that travels the network. It runs the same pre-check first: if the booking is not send-ready it returns 422 with the exact list of violations rather than emitting an invoice an authority would reject. The response Content-Type is application/xml. Before issuance the cbc:ID is a provisional BKG-{id} identifier — see send- readiness rules in Send-readiness.

curl https://api.taxlens.getdynamiq.ai/v1/bookings/123/document/ubl \
  -H "X-API-Key: $TAXLENS_KEY" \
  -o invoice-preview.xml

Issue an invoice

POST /v1/bookings/{id}/issue turns a send-ready booking into an immutable IssuedInvoice. It runs the pre-check, burns a gapless fiscal number (per issuer / document type / series / year), and freezes the EN 16931 projection. It's idempotent — re-issuing returns the same invoice. Returns 422 if not send-ready, 409 if the booking is voided. Set self_billed: true to issue a 389 in the supplier's name (platform/OTA merchant-of-record) on its own fiscal counter — set supplier_legal_issuer_id on the booking first.

FieldTypeDescription
seriesstringOptional numbering series prefix (Italy/Spain/Mexico keep parallel streams). Defaults to none.
self_billedbooleanIssue as a self-billed invoice (UNCL1001 389) instead of a commercial 380. Default false. See Self-billing.
curl -X POST https://api.taxlens.getdynamiq.ai/v1/bookings/123/issue \
  -H "X-API-Key: $TAXLENS_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "series": "" }'
{
  "id": 9,
  "booking_id": 123,
  "legal_issuer_id": 1,
  "document_type": "380",
  "series": "",
  "fiscal_year": 2026,
  "sequence_number": 1,
  "invoice_number": "2026-000001",
  "currency": "EUR",
  "grand_total": "495.00",
  "status": "issued",
  "preceding_invoice_id": null,
  "issued_at": "2026-05-29T10:00:00Z"
}
Issuance freezes the booking
Once a live 380/389 exists, the booking can't be adjusted, voided, refunded, or invoice-detail-edited (all 409). Correct it with a credit note. Details in Numbering & immutability.

Credit note

POST /v1/invoices/{id}/credit-note issues a 381 that reverses an issued invoice: it burns a fiscal number from the credit-note sequence, references the original (BT-25), marks the original credited, and serializes as a PEPPOL CreditNote document. Idempotent — one credit note per booking. After crediting, the original no longer constrains the booking.

curl -X POST https://api.taxlens.getdynamiq.ai/v1/invoices/9/credit-note \
  -H "X-API-Key: $TAXLENS_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "guest cancelled", "series": "" }'

Retrieve issued invoices

GET /v1/invoices lists your org's issued invoices (newest first, cursor paginated; filter by booking_id). GET /v1/invoices/{id} returns one with its frozen document; GET /v1/invoices/{id}/ubl renders the frozen document as UBL with the real fiscal number as cbc:ID (versus the provisional BKG- id of the booking-level preview).

curl "https://api.taxlens.getdynamiq.ai/v1/invoices?booking_id=123" \
  -H "X-API-Key: $TAXLENS_KEY"

curl https://api.taxlens.getdynamiq.ai/v1/invoices/9/ubl \
  -H "X-API-Key: $TAXLENS_KEY" -o invoice-9.xml
GET/v1/invoices
Sign in to run

List your organization's issued invoices (read-only).

Sign in to run this against the live API. Read-only — nothing is saved.

Next

See the dashboard equivalents in Invoices, the full lifecycle in The invoice lifecycle, and what's deferred (clearance / transmission, non-UBL formats) in Coverage & what's deferred. To roll issued tax up for remittance, continue to Reporting API.