LedgerBee Developer
  • Getting started
  • Conventions
  • Products
  • Advisor firms
  • Configuration
  • API Reference
Subscriptions
Payment flowCard paymentsProducts & PricingDiscountsProduct Entitlements
Billing documents
    OverviewInvoicesCredit notesQuotesOrder confirmationsDocument templates
Accounting
WebhooksPartner-Hosted Checkout
Customer Portal
Billing documents

Billing documents

LedgerBee has four customer-facing documents, all built on the same line model and rendered to PDF. Two are booked accounting documents and two are pre-sales documents that convert forward into a booked invoice:

DocumentBooked?Page
Invoiceyes — posts to the books on sendInvoices
Credit noteyes — posts to the books on sendCredit notes
Quoteno — a price offerQuotes
Order confirmationno — confirms an agreed orderOrder confirmations

All endpoints live under https://api.ledgerbee.com/api/v1 and authenticate with the x-api-key header. Full request/response schemas live in the API Reference. This page covers what the four share; each document's own page covers its lifecycle, creation, and type-specific operations.

Scopes

Each resource has its own scope pair (see API Keys). Invoices and credit notes share one pair:

ResourceReadWrite
Invoices & credit notesinvoices-readinvoices-write
Quotesquotes-readquotes-write
Order confirmationsorder-confirmations-readorder-confirmations-write

Read scopes cover list, get, and PDF; write scopes cover create, update, send, and convert.

Document lines

A line is one of two kinds, and what you send depends on the kind. The same rules apply to invoices, credit notes, quotes, and order confirmations.

Line kindHow to send itAmounts & VAT
Product lineSet priceId (required) and quantity. Optionally override unitPrice, and set description for custom line text.Derived server-side: totalPrice = quantity * unitPrice for a flat-rate price or an overridden unitPrice; for a volume, graduated or package price, totalPrice is what the price's tiers charge for the quantity. VAT from the product and the customer's VAT zone. Do not send totalPrice / vatRate / vatAmount.
Text / separator lineSet lineType to text or separator. priceId and quantity are not required for these lines — omit them.None — do not send totalPrice / vatRate / vatAmount.

Every billable line must reference a priceId from the product catalogue. A document must contain at least one product line; text / separator lines may accompany it but do not satisfy the rule on their own. There is no hand-typed amount line — a billable line always bills a catalogue price.

Lines are returned in the order you list them in the lines array: the API stamps your submission order onto each line. There is no separate sort field; to reorder, send the array in the order you want.

Creation does not dedupe retried requests. When a timeout leaves the outcome unknown, list recently created documents before re-creating. See Idempotency.

Text on the document

A document has three free-text fields. Two of them print on the PDF; the third is for your own records.

FieldPrinted on the PDFUse it for
subjectYes, in bold, directly above notesA heading that says what the document is about, such as Website maintenance, March. At most 255 characters.
notesYes, under subjectA message to the customer.
descriptionNeverYour own reference. The customer never sees it. Invoices and credit notes only.

In the standard layout subject and notes print under the customer's address, above the lines.

Code
{ "subject": "Website maintenance, March", "notes": "Thank you for your business." }

On an update, leave subject out to keep it, or send null to clear it. Converting a quote or an order confirmation carries its subject to the document it becomes.

Discounts

A discount is a record in LedgerBee: a name, a type (percentage, fixed or per unit), a scope (line or document) and the products, customers and dates it applies to. A document applies a discount by naming its id in discountIds. There is no hand-typed discount on a line. Creating discount records and assigning them is covered in Discounts.

WhereTakesApplies
A line's discountIdsLine-scope discountsTo that line, in the order sent
The document's discountIdsDocument-scope discountsOnce, after every line discount, to the lines whose product it applies to (every line when it names no product)

Each id is checked for the line's product, the document's customer and the document date. A document converted from a quote is checked at the quote's date. A fixed or per-unit discount takes its amount in the document's currency; a discount with no amount in that currency is refused rather than converted.

Acme buys 2 × Widget at 1,000.00 and 1 × Gadget at 500.00, both at 25 % VAT. "Loyalty 10%" is a line-scope percentage discount assigned to Acme for both products. POST /v1/invoices:

Code
{ "customerId": "0199b6d2-3c1a-7a40-9f00-1d2e3f4a5b01", "date": "2026-12-01", "lines": [ { "priceId": "0199b6d2-3c1a-7a40-9f00-1d2e3f4a5b02", "quantity": 2, "discountIds": ["0199b6d2-3c1a-7a40-9f00-1d2e3f4a5b04"] }, { "priceId": "0199b6d2-3c1a-7a40-9f00-1d2e3f4a5b03", "quantity": 1, "discountIds": ["0199b6d2-3c1a-7a40-9f00-1d2e3f4a5b04"] } ] }

The invoice comes to 2,250.00 net and 562.50 VAT. Each line reports what each discount took off in appliedDiscounts, the document reports its document-scope discounts in documentDiscounts, and totalDiscounted is the sum of both:

Code
{ "subtotal": "2250.00", "vat": "562.50", "amount": "2812.50", "totalDiscounted": "250.00", "documentDiscounts": [], "lines": [ { "totalPrice": "2000.00", "appliedDiscounts": [ { "discountId": "0199B6D2-3C1A-7A40-9F00-1D2E3F4A5B04", "position": 0, "name": "Loyalty 10%", "type": "percentage", "percentagePct": "10", "originalDiscountAmount": null, "originalReductionAmount": "200.00" } ] }, { "totalPrice": "500.00", "appliedDiscounts": [ { "discountId": "0199B6D2-3C1A-7A40-9F00-1D2E3F4A5B04", "position": 0, "name": "Loyalty 10%", "type": "percentage", "percentagePct": "10", "originalDiscountAmount": null, "originalReductionAmount": "50.00" } ] } ] }

A line's totalPrice stays its amount before discounts. totalDiscounted also counts a line discount typed by hand in LedgerBee, and is null on a document that names no discount record.

On an update, a line's discountIds travel with the restated lines: a line sent without them has no line discounts. The document's discountIds stay as they are when left out, and an empty array removes them. A credit note that names an invoice takes no discounts of its own. It gives back the discounts that invoice used on each line that names the invoice line it credits — see Credit specific invoice lines and Credit a whole invoice.

A discount that cannot be applied refuses the request with 400:

CodeCause
DISCOUNT_NOT_APPLICABLENo such discount, inactive, not assigned to this product and customer, or outside its dates. details.reason says which.
DISCOUNT_WRONG_SCOPEA document-scope id on a line, or a line-scope id on the document.
DISCOUNT_REFERENCED_TWICEThe same id twice on one line or on the document.
DISCOUNT_NOT_COMBINABLEA discount that may not share a line with another.
DISCOUNT_NO_AMOUNT_IN_CURRENCYA fixed or per-unit discount with no amount in the document's currency.
DISCOUNT_LINE_WITHOUT_PRODUCTA discount on a text or separator line.
DISCOUNT_EXCEEDS_LINE / DISCOUNT_EXCEEDS_DOCUMENTThe discounts would take the line or the document below zero.
DISCOUNT_REPRICE_NEEDS_LINESAn invoice update changed its discounts, or the customer, date or currency of an invoice carrying them, without sending lines.
DISCOUNT_CREDIT_NOTE_FOLLOWS_INVOICEA credit note that names invoiceNumber sent discount ids.

Reading documents

Every resource exposes the same read surface:

ActionPath
ListGET /v1/{resource}
Get oneGET /v1/{resource}/{id}
Download PDFGET /v1/{resource}/{id}/pdf

Lists paginate (page, default 1; limit, default 25, max 100) and filter by customerId and status (plus free-text search on invoices/credit notes, and sortBy / sortOrder on quotes/order confirmations). The status filter matches the display status, so server-derived states work too — each document page lists its own status values. List items omit the lines breakdown and the discounts; get a single document to read them. Every {id} path segment is a UUID.

GET /v1/{resource}/{id}/pdf streams application/pdf as an attachment. Pass ?draft=true to render a watermarked draft preview before the document is sent.

Sending

POST /v1/{resource}/{id}/send allocates the document number (if not already set), renders the PDF, and delivers it over the chosen channel. The body's deliveryType selects the channel:

deliveryTypeBehaviour
EmailEmails the rendered PDF. The recipient resolves from the request's email field, else the customer contact, else the customer record.
SproomInvoices and credit notes only. Delivers over the Sproom e-invoicing network. There is no legal e-invoice type for a quote or order confirmation.
ManualAllocates the number without delivering. Download the PDF and deliver it yourself.
LeverandorserviceInvoices only. Collects the amount by Danish direct debit over Leverandørservice. The collection request is generated on send; the customer is emailed the invoice — carrying a "will be collected, do not pay manually" notice — once Mastercard accepts the collection. It requires your company to be configured as a creditor, the customer to be enrolled with payment details, and an active mandate — otherwise the send fails with LS_CREDITOR_NOT_CONFIGURED, LS_CUSTOMER_NOT_SET_UP, LS_PAYMENT_DETAILS_REQUIRED, or LS_MANDATE_REQUIRED.

When you send by email to an address that isn't already a contact of the customer, that email is created as a customer contact and linked to the document (matching the gated app); an email that already matches a contact reuses it. documentTemplateId overrides the PDF template for a single send. The exact recipient field names differ by resource — see each document's page.

Errors

ConditionResponseFix
Invalid request (e.g. non-UUID {id})400Pass a UUID id and a body matching the schema.
Invalid or missing API key401Send a valid x-api-key header.
API key missing required scopes403Grant the key the resource's read / write scope.
Email send with no resolvable recipientthe send failsPass the recipient email, or set an email on the customer / contact.

Type-specific errors (conversion, locked-after-convert, …) are on each document's page.

Last modified on November 6, 2026
Product EntitlementsInvoices
On this page
  • Scopes
  • Document lines
  • Text on the document
  • Discounts
  • Reading documents
  • Sending
  • Errors
JSON
JSON
JSON