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.
By Matthias Begot ·
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 — 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.
- 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.
- 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.
- 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:
POST /invoice/late-payment
Content-Type: application/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):
{
"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, 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
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.
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:
{
"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_sourcestaysstatutory, and a note explains it. An agreed rate above it is applied tranche by tranche withrate_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:
{
"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" }
]
}
{
"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:
- Discover the endpoint on the catalog
and call it; receive the
402. - Pay — sign and replay the request.
- Read
total_due, and keepperiods[]andrule.legal_basisin the audit trail: they are what a debtor’s counsel will ask for. - Branch — on
total_due: null, decide the FX policy yourself; onrule_source: "eu_minimum_fallback", decide whether a floor is good enough for the amount at stake.
The 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 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— the date the term falls on./legal/deadlineproduces the date;/invoice/late-paymentprices the breach of it.POST /invoice/validate— is the invoice you are chasing conformant in the first place, before you build a claim on it?GET /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 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; for how agents
discover and call Invoket endpoints, see For agents.
Sources and attribution
- Directive 2011/7/EU on combating late payment in commercial transactions (recast) — 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 and article D441-5 — Légifrance, DILA.
- § 288 BGB and § 247 BGB (Basiszinssatz) — Gesetze im Internet, Bundesministerium der Justiz.
- Bekanntgabe des Basiszinssatzes zum 1. Juli 2025 — 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 — Moniteur belge, as amended on 22 November 2013.
- Ustawa z dnia 8 marca 2013 r. o przeciwdziałaniu nadmiernym opóźnieniom w transakcjach handlowych — ISAP, Sejm RP, art. 10 (tiered compensation since 1 January 2020).
- Decreto legislativo 9 ottobre 2002, n. 231 — Normattiva, as amended by d.lgs. 192/2012.
- Proposal for a Regulation on combating late payment in commercial transactions, COM(2023) 533 and its procedure file 2023/0323(COD) — 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.