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.

  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.

RuleInstrumentWhat it fixes
Statutory interestDirective 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 resetDirective 2011/7/EU, art. 3(2)The rate in force on 1 January, then on 1 July
Default payment periodDirective 2011/7/EU, art. 3(3)30 calendar days where the contract is silent
Cap on agreed termsDirective 2011/7/EU, art. 3(5)60 calendar days, unless expressly agreed and not grossly unfair
Recovery costsDirective 2011/7/EU, art. 6A 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_sourceYou supplyBehaviour
contractual_due_datedue_dateUsed as stipulated
payment_termsinvoice_date + payment_terms_daysTerm counted from the invoice date
statutory_defaultinvoice_date aloneThe 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.

countryrule_sourcereference_basisMarginNational specificity
FRnationalecb10 ptsCode de commerce, art. L441-10, L441-11, D441-5
DEnationalecb9 pts§ 288(2) BGB, on the Basiszinssatz base of § 247 BGB
BEnationalecb8 ptsStatutory rate rounded up to the half point
ES, IT, NL, PTnationalecb8 ptsOwn transposition, Directive margin
PLnationalnbp10 ptsNBP reference rate; fixed sum tiered by claim size
Any other EU Stateeu_minimum_fallbackecb8 ptsDirective 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.sourceflat, 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_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:

{
  "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:

  1. Discover the endpoint on the 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 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/deadline produces the date; /invoice/late-payment prices 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

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.