Notable changes to the Lucanto API — new endpoints, fields, and breaking changes. Filter entries by tag and subscribe via RSS.
Changed: invoice PDFs are ISDOC.PDF where ISDOC applies
GET /workspaces/{workspace_id}/invoices/{id}/pdf now returns an ISDOC.PDF
for an issued invoice in a Slovak or Czech workspace: a PDF/A-3 that looks
and prints like before, with the ISDOC XML embedded so accounting software
such as POHODA, KROS or Money can import it. The response is still
application/pdf; the filename ends in .isdoc.pdf.
Drafts, cancelled invoices, other workspace countries, and any invoice
whose ISDOC cannot be built get the plain PDF, named .pdf, as before.
?variant=plain forces the plain PDF, and plain is the only accepted
value. The ETag now differs per variant, so a cached plain PDF is not
served as the default. The MCP lucanto_get_invoice_pdf_url tool names
the file the same way.
Added: German (de) as a user and document language
language on GET /user and PATCH /user now accepts and returns de.
Unlike uk, German is also a document language: pdf_language on invoices,
credit notes, proformas and quotes, the locale on the send-e-mail bodies,
and the document-settings defaults accept de, so PDFs and customer e-mails
can be rendered in German.
Added: manual reminders for proformas
POST /workspaces/{workspace_id}/proformas/{id}/send_reminder sends a
payment reminder for an issued, unpaid proforma, like the invoice endpoint.
It needs the manage:proformas scope and the same plan and trial gates as
send.
Changed: reminders are queued and limited to one per document per UTC day
POST …/invoices/{id}/send_reminder and the new proforma endpoint no longer
send the e-mail during the request. A 200 means the reminder was accepted
and queued; reminder_count and last_reminded_at are updated once it is
actually sent, so the document in the response does not show them
incremented yet. The limit is one reminder per document per UTC calendar
day, replacing the rolling 24-hour window; a second call the same day
returns 429 with retry_after_seconds counting down to UTC midnight.
Changed: mark_paid defaults paid_on to the workspace's day
When paid_on is omitted on POST …/invoices/{id}/mark_paid or POST …/proformas/{id}/mark_paid, the payment is dated today in the workspace’s
time_zone rather than the server’s UTC date. A payment recorded shortly
after midnight local time now lands on the correct day.
Added: fifty MCP tools across documents, expenses, banking and projects
The MCP server now covers quotes, proformas and credit notes (list, get, create, update), sales orders (list, get), expenses (list, get, create, update), projects (list, get, create, update), contacts (get, update), document settings (get, update), and read-only bank accounts, transactions and loans. Each tool needs the matching resource scope; lifecycle actions for types other than invoices stay on the REST API. The full catalogue is on the MCP server page.
Added: read:loans scope
Tokens can carry read:loans, used by the MCP loan tools. Loans are
read-only: write:loans and manage:loans are accepted but grant only
read access.
Added: Claude.ai sign-in to the MCP server with OAuth 2.1
Claude.ai (web, mobile and Cowork) connects to
https://app.lucanto.eu/api/mcp/v1 by signing the user in rather than
using a pasted token. The user approves the requested access on a consent
screen, capped by their workspace role, and the connection appears under
Settings > Personal API access, where it can be revoked. OAuth access
tokens work only on the MCP endpoint, not on the REST API; personal access
tokens keep working on both.
Added: Ukrainian (uk) as a user language
language on GET /user and PATCH /user now accepts and returns uk
alongside sk, cz, en and ro. It sets the language of the app and of
the e-mails Lucanto sends to that user.
Document languages are unchanged: pdf_language on invoices, credit notes,
proformas and quotes, and locale on the send-e-mail bodies and the MCP
lucanto_send_invoice tool, still take sk, cz, en or ro. A user whose
language is uk receives PDFs and customer e-mails in the document’s own
language; a document without one falls back to the workspace’s country
language, never to the user’s UI language.
Breaking: create/update bodies for invoices, credit notes, proformas and quotes reject unknown fields
InvoiceWritable, CreditNoteWritable, ProformaWritable, QuoteWritable,
their LineItemInput shapes, and the *SendRequest bodies (including the
nested invoice_email / credit_note_email / etc. objects) now declare
additionalProperties: false. A request carrying a key outside the
documented schema is rejected with 422 instead of being silently ignored.
The legacy { "document": { ... } } wrapper and the Rails-style indexed
line_items object ({"0": {...}}) are still accepted by the server for
backward compatibility, but neither is part of the documented schema — send
a flat top-level body with line_items as an array. File uploads
(attachments) travel as multipart form fields, outside this JSON body.
Breaking: line-item removal uses a boolean _destroy
_destroy on InvoiceLineItemInput, CreditNoteLineItemInput,
ProformaLineItemInput, and QuoteLineItemInput is now type: boolean.
The previous "true"/"false" string enum is no longer part of the
documented schema — send true to remove a line item on update.
Breaking: mark_paid.amount is a decimal string with documented over/under-payment semantics
amount on POST …/invoices/{id}/mark_paid and POST …/proformas/{id}/mark_paid must match ^\d+(\.\d{1,2})?$ — no thousands
separators, no comma decimal separator. Omitting it, or sending an explicit
null, settles the whole outstanding balance and payment_status derives
to paid. A smaller amount records a partial payment: payment_status
becomes partially_paid and amount_due drops by that amount, so you can
call the endpoint again for the next instalment. An amount above the
outstanding balance is accepted and recorded in full, and payment_status
then derives to overpaid.
Breaking: MCP lucanto_send_invoice follows the plan and trial gates
Sending an invoice by email over the MCP server is now gated the same way
as the web app and the REST API. On a plan without email sending the tool
returns a subscription error with reason: "feature_not_available"; a
trial workspace can send one document email per day and gets
reason: "trial_send_cap" once that is used up. The daily counter is
shared across all three surfaces, and a request that fails validation does
not consume it.
Added: read-only sales orders API
GET /workspaces/{workspace_id}/sales_orders and GET …/sales_orders/{id} return a customer’s orders to you, tracked until fully
invoiced. Both need the read:sales_orders scope; the resource is
read-only, so the token scope picker offers it at the read level only. Reference the id as sales_order_id on an invoice or proforma to
record which order it fulfils. A sales order carries no payment
instructions: payment_status, amount_paid, and amount_due are always
null, and overdue/paid_late are always null too. status moves
through draft → confirmed (number minted) → fulfilled (system
transition once every ordered line is invoiced, reversible if the invoiced
total drops), plus rejected, reopened, and canceled.
Added: delivery date, texts and item descriptions over MCP
lucanto_create_invoice and lucanto_update_invoice now accept
delivery_date, opening_text, closing_text, and a per-item
description, matching the REST create and update bodies. Invoices imported
through an MCP client no longer lose these fields on the way in.
Fixed: issuing without a number series, email language, and mark_paid errors
POST …/invoices/{id}/issueand the MCPlucanto_issue_invoicetool now answer422with “No number series is set up for this document type. Supply a document number or create a number series.” instead of a generic transition error when the workspace has no series for that document type.- Document emails sent over the API and MCP are rendered in the document’s
language; they no longer fall back to English. An explicit
localeon the send request still overrides it, andlocale: nullkeeps the default. mark_paid’s422now names an invalidamount(not a decimal string with at most two decimals) as a documented failure cause, alongside no outstanding balance, abank_account_idoutside the workspace, and a transaction that fails validation.
Statutory exchange-rate anchors, and ČNB rates for Czech workspaces
Automatically fetched exchange_rate values on foreign-currency documents
and expenses now follow the statutory anchor: the case day is the delivery
date (falling back to the issue date), valued for Slovak workspaces at the
ECB reference rate declared the preceding day, and for Czech workspaces at
the ČNB rate for the case day itself. Czech workspaces can also elect the
D−1 ČNB rate or a fixed rate list (pevný kurz) per workspace, and the
settings screen generates the matching internal directive text.
Breaking: document status is now lifecycle-only; payment state moves to payment_status
The single status enum on invoices, proformas, credit notes, and quotes
no longer mixes lifecycle and payment. status now carries only the
document’s lifecycle, with a per-type value set:
- invoices, credit notes:
draft,issued,sent,canceled - proformas: adds
settledandexpired - quotes:
draft,issued,sent,accepted,rejected,expired,canceled
Breaking: status is no longer writable — use action endpoints
Create and update bodies no longer accept a status field. Documents are
moved through their lifecycle with the action endpoints — POST …/invoices/{id}/issue, POST …/credit_notes/{id}/issue, POST …/quotes/{id}/accept — and payment is recorded, not declared: POST …/invoices/{id}/mark_paid creates a real payment record for the
outstanding balance, and payment_status derives from it.
Settle documents across currencies
POST /workspaces/{workspace_id}/transactions/{id}/pair now accepts
document_amount — the settled amount in the paired record’s own currency —
so a transaction can settle a document or expense held in a different
currency (a EUR card charge paying a USD invoice). The allocation stores both
legs: the bank amount in the transaction’s currency and the settled amount in
the document’s, and payment status derives from the settled side. Decimal
input is locale-tolerant (72,90 and 72.90 both parse).
