# Price an unpaid invoice under the Late Payment Directive > An agent chasing a late B2B invoice needs a number, not a rate. One call splits statutory interest into half-year tranches and adds the article 6 fixed sum. Published 2026-08-10 · HTML version: https://invoket.com/blog/price-an-unpaid-invoice-under-the-late-payment-directive --- An agent that sends a dunning letter, books a receivable or opens a recovery file has to commit to an **amount**. "This invoice is 330 days late at around twelve percent" is not an amount — it is a sentence that hides at least three rules the agent was never given. One paid call — [`POST /invoice/late-payment`](/docs/api/invoice-late-payment) — returns the statutory interest due under **Directive 2011/7/EU**, split **tranche by tranche** across every half-year it crosses, plus the fixed compensation for recovery costs, under the **national transposition** that actually applies. Every tranche carries its own reference rate, statutory rate, day count and interest, so the total can be recomputed by hand. ## Why `amount × rate × days / 365` is wrong The naive formula fails for three independent reasons, and each one is written law rather than convention. 1. **The reference rate resets twice a year.** Article 3(2) of the Directive fixes it for the first semester at the rate in force on **1 January**, and for the second at the rate in force on **1 July**. A claim outstanding for eleven months therefore lives under two or three different rates, not one. 2. **The margin is national.** Article 2(6) sets a floor of "the reference rate and **at least** eight percentage points" — a floor Member States are free to exceed. France and Poland went to ten. 3. **The base is not always the ECB rate.** Germany computes on the *Basiszinssatz* of § 247 BGB, Belgium rounds the result up to the half point, Poland sits on the Narodowy Bank Polski reference rate instead. Get any of the three wrong and the output is still a plausible-looking euro figure. Nothing throws. That is precisely the failure profile an autonomous agent should not absorb into its own reasoning — and it is why the endpoint returns the *decomposition*, not only the total. | Rule | Instrument | What it fixes | |---|---|---| | Statutory interest | Directive 2011/7/EU, art. 2(6) and 3(1) | Reference rate + at least 8 points, simple, from the day after the due date | | Half-year reset | Directive 2011/7/EU, art. 3(2) | The rate in force on 1 January, then on 1 July | | Default payment period | Directive 2011/7/EU, art. 3(3) | 30 calendar days where the contract is silent | | Cap on agreed terms | Directive 2011/7/EU, art. 3(5) | 60 calendar days, unless expressly agreed and not grossly unfair | | Recovery costs | Directive 2011/7/EU, art. 6 | A fixed sum of at least EUR 40, due **as of right** | ## The call A principal, a currency, the Member State whose transposition applies, and the dates. EUR 25,000 due on 31 August 2025, paid on 27 July 2026 in France: ```http POST /invoice/late-payment Content-Type: application/json ``` ```json { "amount": "25000", "currency": "EUR", "country": "FR", "due_date": "2025-08-31", "paid_date": "2026-07-27" } ``` The claim crosses three half-years, so it is served as three tranches (abridged — the constant `limits[]` and the full `rule.legal_basis` travel on every answer): ```json { "data": { "due_date": "2025-08-31", "due_date_source": "contractual_due_date", "paid_date": "2026-07-27", "days_late": 330, "day_count_convention": "calendar days, actual/365", "periods": [ { "from": "2025-09-01", "to": "2025-12-31", "days": 122, "reference_basis": "ecb", "reference_rate": "2.15", "statutory_rate": "12.15", "applied_rate": "12.15", "rate_source": "statutory", "interest": "1015.27" }, { "from": "2026-01-01", "to": "2026-06-30", "days": 181, "reference_basis": "ecb", "reference_rate": "2.15", "statutory_rate": "12.15", "applied_rate": "12.15", "rate_source": "statutory", "interest": "1506.27" }, { "from": "2026-07-01", "to": "2026-07-27", "days": 27, "reference_basis": "ecb", "reference_rate": "2.40", "statutory_rate": "12.40", "applied_rate": "12.40", "rate_source": "statutory", "interest": "229.32" } ], "interest_total": { "amount": "2750.86", "currency": "EUR" }, "flat_fee": { "amount": { "amount": "40", "currency": "EUR" }, "source": "flat" }, "total_due": { "amount": "2790.86", "currency": "EUR" }, "rule": { "country": "FR", "rule_source": "national", "reference_basis": "ecb", "margin_points": "10", "default_payment_term_days": 30, "max_payment_term_days": 60 }, "notes": [] }, "provenance": { "source": "late-payment-snapshots", "freshness": { "kind": "snapshot", "as_of": "2026-07-27T00:00:00Z" } } } ``` `122 + 181 + 27 = 330`, and `1015.27 + 1506.27 + 229.32 = 2750.86`. Each line is `amount × applied_rate × days / 365` and nothing else: interest is **simple, never capitalised**, on an `actual/365` calendar-day basis restated in `day_count_convention` on every answer. A finance team can audit the figure with a pocket calculator, which is the difference between a number an agent *produced* and a number an agent can *defend*. Note where the count starts: **the day after** the due date (art. 3(1)), to the payment date **included**. ## Three ways to say when the payment period ended The due date is the input most often missing from the data an agent holds, so there are three ways to supply it — and `data.due_date_source` always says which one was used. | `due_date_source` | You supply | Behaviour | |---|---|---| | `contractual_due_date` | `due_date` | Used as stipulated | | `payment_terms` | `invoice_date` + `payment_terms_days` | Term counted from the invoice date | | `statutory_default` | `invoice_date` alone | The national statutory period applies, and a `note` says so | Supplying **none** of the three is a `400`, not a guess: a due date is never invented. Supplying both a due date and a term is not an error — the stipulated date wins, and a note records that the term was ignored. If the term itself has to be *computed* — a notice period, a filing date, a contractual deadline in working days across public holidays — that is the job of [`GET /legal/deadline`](/docs/api/legal-deadline), which produces the date this endpoint then prices. And if the claim starts life as an inbound XML document rather than a row in a ledger, [`POST /invoice/read`](/docs/api/invoice-read) extracts the amount, currency and due date to post here. ## The national rule is applied, or the fallback is flagged Eight Member States carry their own transposition — margin, fixed sum, statutory period, legal cap on terms, and their national legal basis quoted in its own language. | `country` | `rule_source` | `reference_basis` | Margin | National specificity | |---|---|---|---|---| | `FR` | `national` | `ecb` | 10 pts | Code de commerce, art. L441-10, L441-11, D441-5 | | `DE` | `national` | `ecb` | 9 pts | § 288(2) BGB, on the *Basiszinssatz* base of § 247 BGB | | `BE` | `national` | `ecb` | 8 pts | Statutory rate rounded **up to the half point** | | `ES`, `IT`, `NL`, `PT` | `national` | `ecb` | 8 pts | Own transposition, Directive margin | | `PL` | `national` | `nbp` | 10 pts | NBP reference rate; fixed sum **tiered** by claim size | | Any other EU State | `eu_minimum_fallback` | `ecb` | 8 pts | Directive minimum, explicitly flagged | The German case shows why the base cannot be assumed. On the same tranche where the ECB reference rate is served as `2.15`, a German claim is served a `statutory_rate` of `10.27` — 9 points over a *Basiszinssatz* of 1.27, the value the Bundesbank announced for the half-year, which sits below the ECB rate rather than equal to it. Nine plus 2.15 would have been wrong by more than eight tenths of a point, in the creditor's favour, on every day of the claim. For a Member State without its own rule in the service, `rule_source` is `eu_minimum_fallback` and the answer says so. That flag is a **floor**, not a national rule: the real national rule may be more generous to the creditor. An agent that reads it knows the difference between "this is the law of that State" and "this is the least the Directive guarantees" — a distinction most tables of statutory interest quietly lose. Whether a directive has in fact been transposed in a given State, and by which national measures, is its own question: [`GET /legal/transposition`](/docs/api/legal-transposition). ## The fixed sum, and the one case where there is no total The EUR 40 of article 6 is due **as of right**, without any reminder, as soon as interest is due. Poland tiers it by claim size, and the response says which regime produced the figure through `flat_fee.source` — `flat`, `tiered`, or `not_due` when nothing is late. A Polish claim of PLN 120,000, due 15 January 2026: ```json { "days_late": 193, "periods": [ { "from": "2026-01-16", "to": "2026-06-30", "days": 166, "reference_basis": "nbp", "reference_rate": "4.00", "statutory_rate": "14.00", "applied_rate": "14.00", "interest": "7640.55" }, { "from": "2026-07-01", "to": "2026-07-27", "days": 27, "reference_basis": "nbp", "reference_rate": "3.75", "statutory_rate": "13.75", "applied_rate": "13.75", "interest": "1220.55" } ], "interest_total": { "amount": "8861.10", "currency": "PLN" }, "flat_fee": { "amount": { "amount": "100", "currency": "EUR" }, "source": "tiered" }, "total_due": null, "notes": [ "interest is computed in PLN, while the fixed recovery sum of art. 6 is denominated in euro: total_due is not served because no exchange rate is applied" ] } ``` Two things are worth reading twice. The tier is **EUR 100**, not EUR 40 — above PLN 50,000 the Polish law raises the sum, and a Directive-minimum table would have understated the claim by sixty euros. And `total_due` is **`null`**: interest is in zlotys, the fixed sum is a euro amount fixed by the Directive, and the service does not invent an exchange rate to make the two addable. A `null` with a stated reason is a better input to an agent than a total computed on an unstated FX assumption. ## The notes that change the number Four situations are **recorded** rather than silently applied — each one a place where a plain calculator would have produced a different figure without saying so. - **An agreed rate below the statutory one.** The statutory rate is a public-policy floor (art. 7): the agreed rate is ignored, `rate_source` stays `statutory`, and a note explains it. An agreed rate *above* it is applied tranche by tranche with `rate_source: "contractual"`. - **No term supplied.** The national statutory period applies — 30 days in the covered States — quoted with its national basis. - **An agreed term beyond the legal cap.** A 90-day term where the cap is 60 is **not refused**. It is computed as supplied, with a note recalling that such a term may be void, in which case the statutory period applies. The service computes; it does not rule on the validity of a contract clause. - **Both a due date and a term.** The stipulated due date is used, the term is ignored, and the note records it. That last distinction is the editorial line of the whole endpoint. An agent must not be handed a verdict on a contested clause dressed up as arithmetic — it must be handed the arithmetic *and* the flag that says a lawyer may reach a different figure. ## Zero is a billed answer; a guess is not an answer at all Per the x402 golden rule, the agent pays for the answer to its question. The question is "what does this late invoice owe?", and **zero is a valid answer**: a claim paid on time returns `days_late: 0`, empty `periods`, `flat_fee.source: "not_due"`, `total_due: 0` and a `rationale` citing art. 3(1). That is a computed result — the thing that lets an agent *close* a dispute instead of opening one — so it is a **200**. What the service cannot compute leaves the 200 range and is **not billed**: ```json { "code": "INVALID_INVOICE_INPUT", "issues": [ { "code": "unsupported_country", "message": "US is not an EU member state: Directive 2011/7/EU governs commercial transactions within the Union, and no national rule is guessed outside it", "path": "country" } ] } ``` ```json { "code": "INVALID_INVOICE_INPUT", "issues": [ { "code": "date_out_of_range", "message": "no ecb reference rate is known for 2027-01-01: the committed series covers 2011-01-01 to 2027-01-01 (exclusive) and is never extrapolated", "path": "due_date" } ] } ``` The rate series is **never extrapolated**: the rate of a half-year that has not opened yet does not exist, and a plausible extrapolation of it would be the worst possible output. Faults are accumulated too — one `400` lists every actionable issue in `issues[]`, so a malformed request is fixed in one round trip rather than four. ## What it will not do Six constant sentences travel in `data.limits` on every answer. They are the contract, not small print. Out of scope: **consumer claims** (the Directive governs transactions between undertakings and with public authorities), the sector-specific and public-authority regimes, **recovery costs above the fixed sum** (art. 6(3) — instructing a lawyer or a collection agency), the **limitation** of the claim, judicial interest and contractual penalties other than the rate, and any assessment of whether the debt is owed at all. Nor does it look anyone up: the parties, the debt and its currency are taken as declared. It computes what the law provides on the facts you declare. It does not litigate them. ## Why this does not belong in agent code The obvious objection is that a table of eight national margins is a small constant. It is — until it moves. The reference rate moves twice a year by construction; national transpositions move on their own schedule; and the Commission proposed on 12 September 2023 to replace the Directive itself with a **Regulation** on combating late payment (COM(2023) 533), on which Parliament adopted its position in April 2024 and which remains in the ordinary legislative procedure — not adopted, so the Directive is still the instrument in force. A constant that is one legislative act away from being wrong, inside an agent that sends dunning letters, is a liability with no error to catch. The runtime here is pure computation over a **committed rate series** — no network call and no secret at request time — and `provenance.freshness.as_of` tells you the date of the series that backed the figure. ## Where it sits in the x402 loop Same pattern as every Invoket endpoint — discover, receive the `402`, sign the chosen rail, replay: 1. **Discover** the endpoint on the [catalog](https://api.invoket.com/catalog) and call it; receive the `402`. 2. **Pay** — sign and replay the request. 3. **Read** `total_due`, and keep `periods[]` and `rule.legal_basis` in the audit trail: they are what a debtor's counsel will ask for. 4. **Branch** — on `total_due: null`, decide the FX policy yourself; on `rule_source: "eu_minimum_fallback"`, decide whether a floor is good enough for the amount at stake. The [Quickstart](/docs/quickstart) walks the whole discover → `402` → pay → replay cycle with runnable snippets. Price and accepted rails are deliberately not pinned in this article: the [catalog](https://api.invoket.com/catalog) is the single source of truth for both, and it also states which endpoints currently carry a free trial call. Three endpoints sit naturally around a receivable: - [`GET /legal/deadline`](/docs/api/legal-deadline) — the date the term falls on. `/legal/deadline` produces the date; `/invoice/late-payment` prices the breach of it. - [`POST /invoice/validate`](/docs/api/invoice-validate) — is the invoice you are chasing conformant in the first place, before you build a claim on it? - [`GET /company/events`](/docs/api/company-events) — whether the debtor is already in an insolvency proceeding, before you spend anything on recovery. Used for what it is — a deterministic, auditable figure under a named national rule — [`POST /invoice/late-payment`](/docs/api/invoice-late-payment) takes statutory interest off the list of things an agent has to be trusted to improvise. For the full field reference, the covered transpositions and the error codes, see the [endpoint documentation](/docs/api/invoice-late-payment); for how agents discover and call Invoket endpoints, see [For agents](/docs/for-agents). ## Sources and attribution - [Directive 2011/7/EU on combating late payment in commercial transactions (recast)](https://eur-lex.europa.eu/eli/dir/2011/7/oj/eng) — EUR-Lex, Publications Office of the European Union. Articles 2(6), 2(7), 3(1)–(3), 3(5), 6 and 7. - [Article L441-10 du Code de commerce](https://www.legifrance.gouv.fr/codes/article_lc/LEGIARTI000032227352) and [article D441-5](https://www.legifrance.gouv.fr/codes/article_lc/LEGIARTI000033789726) — Légifrance, DILA. - [§ 288 BGB](https://www.gesetze-im-internet.de/bgb/__288.html) and [§ 247 BGB (*Basiszinssatz*)](https://www.gesetze-im-internet.de/bgb/__247.html) — Gesetze im Internet, Bundesministerium der Justiz. - [Bekanntgabe des Basiszinssatzes zum 1. Juli 2025](https://www.bundesbank.de/de/presse/pressenotizen/bekanntgabe-des-basiszinssatzes-zum-1-juli-2025-anpassung-auf-1-27--959886) — Deutsche Bundesbank, on the half-yearly publication of the *Basiszinssatz*. - [Loi du 2 août 2002 concernant la lutte contre le retard de paiement dans les transactions commerciales](https://www.ejustice.just.fgov.be/cgi_loi/change_lg.pl?language=fr&la=F&cn=2002080232&table_name=loi) — Moniteur belge, as amended on 22 November 2013. - [Ustawa z dnia 8 marca 2013 r. o przeciwdziałaniu nadmiernym opóźnieniom w transakcjach handlowych](https://isap.sejm.gov.pl/isap.nsf/DocDetails.xsp?id=wdu20130000403) — ISAP, Sejm RP, art. 10 (tiered compensation since 1 January 2020). - [Decreto legislativo 9 ottobre 2002, n. 231](https://www.normattiva.it/uri-res/N2Ls?urn:nir:stato:decreto.legislativo:2002-10-09;231) — Normattiva, as amended by d.lgs. 192/2012. - [Proposal for a Regulation on combating late payment in commercial transactions, COM(2023) 533](https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=celex:52023PC0533) and its [procedure file 2023/0323(COD)](https://eur-lex.europa.eu/procedure/EN/2023_323) — EUR-Lex. Reference rates come from a committed series of the **European Central Bank** main refinancing rate and, for Poland, of the **Narodowy Bank Polski** reference rate. The articles of Directive 2011/7/EU cited in `rule.legal_basis` are queryable through [`GET /legal/eu-act`](/docs/api/legal-eu-act).