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.

