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.
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.xmlIssue 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.
| Field | Type | Description |
|---|---|---|
| series | string | Optional numbering series prefix (Italy/Spain/Mexico keep parallel streams). Defaults to none. |
| self_billed | boolean | Issue 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"
}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.xmlList 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.