The invoice lifecycle
A booking starts as a tax calculation and ends as an immutable fiscal document. This is the whole path — calculate, capture the buyer, project to a standard invoice model, validate, serialize, issue, and (for corrections) credit-note.
From a number to a legal document
TaxLens already determines what tax is owed. E-invoicing is the layer that turns that determination into a document a tax authority recognizes — built, validated, and frozen as your system of record.
The flow has a clear shape, the same one used by every serious tax-compliance platform: determine → document → pre-check → export → issue. Each step is a real API call, and each adds something the next one needs.
1 · Calculate and persist the booking
A booking is a saved tax calculation. You POST the same calculation request you'd send to Calculate tax, and TaxLens stores the itemized result with an idempotency key. You can attach the seller's legal issuer here, and supply the buyer up front (buyer_name, buyer_address) — or add them later.
See Bookings (API) and Bookings (dashboard).
2 · Capture the buyer
An invoice needs a named buyer with an address — a calculation does not. If you didn't provide the buyer when you created the booking, set it afterward with PATCH /v1/bookings/{id}/invoice-details. This is invoice-only metadata: it does not re-run the tax engine and does not touch the calculated amounts. In the dashboard this is the "Edit invoice details" form on the booking document modal.
// PATCH /v1/bookings/4821/invoice-details
{
"buyer_name": "Northwind Travel GmbH",
"buyer_address": {
"line1": "Friedrichstraße 12",
"city": "Berlin",
"postal_code": "10117",
"country_code": "DE"
},
"legal_issuer_id": 7
}legal_issuer_id) before the booking is issued. After issuance the booking is frozen and this endpoint returns 409.3 · Project to a standard invoice model
GET /v1/bookings/{id}/document projects the stored booking into an EN 16931 semantic invoice — the country-agnostic European standard for what an invoice means (seller, buyer, lines, per-rate VAT, totals). The response includes a validation block so you can see send-readiness without exporting anything. Learn the standards in EN 16931, UBL & PEPPOL.
4 · Pre-check before you export
The same pre-check that fills the validation block also gates the export. If a mandatory Business Term is missing — no seller VAT registration, no buyer country, no VAT line — the UBL endpoint returns HTTP 422 with the exact list of violations. You never emit an invoice an authority would reject. See Send-readiness & fixing violations.
5 · Serialize to UBL
GET /v1/bookings/{id}/document/ubl serializes the validated document to UBL 2.1 / PEPPOL BIS Billing 3.0 XML — the interoperable wire format. At the booking stage the document carries a provisional id (BKG-{id}); it only gets a real fiscal number once issued.
6 · Issue — the point of no return
POST /v1/bookings/{id}/issue turns a send-ready booking into an immutable IssuedInvoice (document type 380). It runs the pre-check one last time, burns a gapless fiscal number from the issuer's sequence, and freezes the EN 16931 projection into canonical_json. Issuance is idempotent (one document per booking + type) and serialized so concurrent calls can't burn duplicate numbers.
380/389) exists, the booking is frozen: adjust, void, refund, and invoice-details all return 409. This is the whole point of a system of record — a fiscal document can't be quietly edited after the fact.curl -X POST https://api.taxlens.getdynamiq.ai/v1/bookings/4821/issue \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{}'
# → 201 (first issue; an idempotent replay of the same live invoice → 200)
# {
# "id": 9102,
# "booking_id": 4821,
# "document_type": "380",
# "series": "A",
# "fiscal_year": 2026,
# "sequence_number": 145,
# "invoice_number": "A-2026-000145",
# "status": "issued",
# "preceding_invoice_id": null
# }7 · Correct with a credit note
Because issued documents are immutable, you fix a mistake by issuing a reversal, not by editing. POST /v1/invoices/{id}/credit-note issues a 381 against its own fiscal counter, references the original number, reverses its amounts, and marks the original credited. Crediting un-freezes void, refund, and invoice-details on the booking, but it does not let you re-issue the same primary document type: re-issuing another 380 on the credited booking returns 409 ("re-issuance is not supported") because the (booking_id, document_type) slot stays occupied by the credited row. To put out a corrected invoice, issue the credit note, then create a new booking and issue a fresh invoice against it. (The other primary type is still free — a credited 389 leaves the 380 slot open.)
curl -X POST https://api.taxlens.getdynamiq.ai/v1/invoices/9102/credit-note \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "reason": "Rate corrected after re-grading the room" }'
# → 201 (an idempotent replay of the same credit note → 200)
# {
# "id": 9118,
# "document_type": "381",
# "invoice_number": "C-2026-000031",
# "preceding_invoice_id": 9102,
# "status": "issued"
# }The full numbering and freeze rules live in Fiscal numbering & immutability. When a platform issues in the supplier's name, see Self-billing (389).
Where to go next
You can browse and download every issued document in the Invoices dashboard, and call the endpoints from E-invoicing (API). For the honest boundary of what's produced today versus what's still on the roadmap, read What's covered vs deferred.