legal.disbursements-vat Unreviewed
Whether a cost passed to a client is a VAT disbursement (outside scope) or a recharge (VAT due), per HMRC Notice 700.
1.0.1 · published 2026-10-03 by charlie · Anterra
Pinned by 14 tests, run in TypeScript, Python and Rust.
Unreviewed. This capability’s implementations agree in every language and pass its published test vectors, which were worked out from the official sources cited. But no qualified solicitor has yet checked those vectors, or confirmed that the capability covers the cases it claims. Treat it as a draft. Do not use it for real people, money or decisions without your own expert review. Once a qualified reviewer signs off, this notice is replaced with their name, qualification and the date. Each new version needs fresh sign-off.
Not professional advice. This capability calculates legal figures from published rules. It is a software component for developers, not legal advice. Rules change and every rate here has an effective date. Check that the dates cover your case. Verify results against the official sources listed in its README, and have a solicitor review how you use it, before anyone relies on the output. Provided “as is” under its licence, without warranty.
What it does
Solicitors, accountants and other professionals pass costs on to clients all the time, and the VAT question is always the same: is it a **disbursement for VAT purposes** (paid as the client's agent, left out of the value of your supply, no VAT added by you) or a **recharge** (a cost of your own supply, included in its value, so VAT is charged on it at the rate of your main supply, usually the standard rate, even if the original cost had no VAT)?
HMRC's answer is a checklist: a payment to a third party may be treated as a disbursement only if **all** of these are met (VAT Notice 700, section 25.1.1):
For example
disbursement_vat(acted as agent true, client received and used true, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised true, exact a…)→ treatment disbursement, failed conditions , reason all eight conditions of VAT Notice 700 section 25.1.1 are met: pass the cost on at the exact amount, outside the value of your s… a court fee paid on the client's behalf with their authority, itemised at cost: a disbursementdisbursement_vat(acted as agent false, client received and used true, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised true, exact …)→ treatment recharge, failed conditions actedAsAgent, reason not a disbursement for VAT purposes (actedAsAgent not met): include the cost in the value of your supply and charge VAT … actedAsAgent alone failing makes it a rechargedisbursement_vat(acted as agent true, client received and used false, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised true, exact …)→ treatment recharge, failed conditions clientReceivedAndUsed, reason not a disbursement for VAT purposes (clientReceivedAndUsed not met): include the cost in the value of your supp… clientReceivedAndUsed alone failing makes it a recharge
The function
The same function in TypeScript, Python and Rust, pinned by the same tests. Pick your language; the choice follows you around the registry.
def disbursement_vat(facts: DisbursementFacts) -> DisbursementTreatment
| facts | DisbursementFacts | |
| returns | DisbursementTreatment |
The types it declares, generated into your project
@dataclass(frozen=True)
class DisbursementFacts:
"""The eight conditions of HMRC VAT Notice 700 section 25.1.1, each true when met."""
#: you acted as your client's agent when you paid the third party
acted_as_agent: bool
#: your client actually received and used the goods or services
client_received_and_used: bool
#: your client was responsible for paying the third party
client_responsible_to_pay: bool
#: your client authorised you to make the payment on their behalf
client_authorised_payment: bool
#: your client knew a third party would provide the goods or services
client_knew_third_party: bool
#: the outlay is separately itemised on your invoice
separately_itemised: bool
#: you recover only the exact amount you paid
exact_amount_recovered: bool
#: the goods or services are clearly additional to your own supply
additional_to_own_supply: bool
VatCostTreatment = Literal["disbursement", "recharge"]
@dataclass(frozen=True)
class DisbursementTreatment:
"""How to invoice the cost, and why."""
#: disbursement: outside the scope of your VAT; recharge: part of your supply, VAT at its rate
treatment: VatCostTreatment
#: the names of the conditions not met, in HMRC's order; empty for a disbursement
failed_conditions: List[str]
reason: str
Your code names it in one line, in the file that uses it
from fune.legal.disbursements_vat import disbursement_vat # legal.disbursements-vat@^1
from typing import List
from .legal_disbursements_vat_types import DisbursementFacts, DisbursementTreatment
# HMRC VAT Notice 700 section 25.1.1, in HMRC's order. All must be met.
_CONDITIONS = (
("actedAsAgent", "acted_as_agent"),
("clientReceivedAndUsed", "client_received_and_used"),
("clientResponsibleToPay", "client_responsible_to_pay"),
("clientAuthorisedPayment", "client_authorised_payment"),
("clientKnewThirdParty", "client_knew_third_party"),
("separatelyItemised", "separately_itemised"),
("exactAmountRecovered", "exact_amount_recovered"),
("additionalToOwnSupply", "additional_to_own_supply"),
)
def disbursement_vat(facts: DisbursementFacts) -> DisbursementTreatment:
"""Disbursement (outside the scope of your VAT) or recharge (VAT due), and why."""
failed: List[str] = []
for name, attr in _CONDITIONS:
value = getattr(facts, attr)
if not isinstance(value, bool):
raise ValueError("%s must be true or false, received %s" % (name, value))
if not value:
failed.append(name)
if not failed:
return DisbursementTreatment(
treatment="disbursement",
failed_conditions=[],
reason="all eight conditions of VAT Notice 700 section 25.1.1 are met: pass the cost on at the exact amount, "
"outside the value of your supply, with no VAT added",
)
return DisbursementTreatment(
treatment="recharge",
failed_conditions=failed,
reason="not a disbursement for VAT purposes (%s not met): include the cost in the value of your supply "
"and charge VAT on it at the rate of your main supply" % (", ".join(failed),),
)Install
fune build
With that line in your source, in a Python project (language python in fune.project), fune build resolves it and nothing else, pins them in fune.lock, downloads only the Python package of each, and builds the code above into your project’s .fune/build, one readable file per capability with a header linking back here. Or pin a range in fune.project and build in one step:
fune add legal.disbursements-vat
The manifest, vectors and README with only the Python implementation. Install it without the registry with fune add ./legal.disbursements-vat-1.0.1-python.fune, or fetch it from a terminal with fune pull legal.disbursements-vat@1.0.1:python.
The whole function, every language, is one file too: legal.disbursements-vat-1.0.1.fune, 24,265 bytes, sha256 f395722568fd97d13528843cefafa35d4e2f217ccc31ee0d5ac5bdbe53b13f21. It installs into a project of any language.
Customise it in your app
The seams this capability offers. Put a marker directly above a function of your own and fune build wires it into the built code; the package on the registry is not changed, the built file’s header lists it under CUSTOMISED, and fune hooks lists every hook in the project. How hooks work.
before — your function gets the arguments and returns them, changed or not, or throws to refuse the call.
# fune: before legal.disbursements-vat
after — your function gets the result and the arguments, and returns the final result.
# fune: after legal.disbursements-vat
replace — it requires no other capability, so there is no dependency to replace.
step — your function runs at a numbered point inside the function’s body, receives the in-scope values it names as parameters, and may return replacements. List the points with fune show legal.disbursements-vat --steps.
# fune: step legal.disbursements-vat after <n|label>
Tests
A version published now needs at least 8 tests for every function, and one that expects the error for each function that throws; the registry refuses it otherwise. fune verify --all runs each case in TypeScript, Python and Rust, and a project runs them again with fune verify. This page lists the cases; it does not run them. The exact JSON is vectors.json.
| Case | Arguments | Expected | |
|---|---|---|---|
| a court fee paid on the client's behalf with their authority, itemised at cost: a disbursement | acted as agent true, client received and used true, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised true, exact a… | → | treatment disbursement, failed conditions , reason all eight conditions of VAT Notice 700 section 25.1.1 are met: pass the cost on at the exact amount, outside the value of your s… |
| actedAsAgent alone failing makes it a recharge | acted as agent false, client received and used true, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised true, exact … | → | treatment recharge, failed conditions actedAsAgent, reason not a disbursement for VAT purposes (actedAsAgent not met): include the cost in the value of your supply and charge VAT … |
| clientReceivedAndUsed alone failing makes it a recharge | acted as agent true, client received and used false, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised true, exact … | → | treatment recharge, failed conditions clientReceivedAndUsed, reason not a disbursement for VAT purposes (clientReceivedAndUsed not met): include the cost in the value of your supp… |
| clientResponsibleToPay alone failing makes it a recharge | acted as agent true, client received and used true, client responsible to pay false, client authorised payment true, client knew third party true, separately itemised true, exact … | → | treatment recharge, failed conditions clientResponsibleToPay, reason not a disbursement for VAT purposes (clientResponsibleToPay not met): include the cost in the value of your su… |
| clientAuthorisedPayment alone failing makes it a recharge | acted as agent true, client received and used true, client responsible to pay true, client authorised payment false, client knew third party true, separately itemised true, exact … | → | treatment recharge, failed conditions clientAuthorisedPayment, reason not a disbursement for VAT purposes (clientAuthorisedPayment not met): include the cost in the value of your … |
| clientKnewThirdParty alone failing makes it a recharge | acted as agent true, client received and used true, client responsible to pay true, client authorised payment true, client knew third party false, separately itemised true, exact … | → | treatment recharge, failed conditions clientKnewThirdParty, reason not a disbursement for VAT purposes (clientKnewThirdParty not met): include the cost in the value of your supply… |
| separatelyItemised alone failing makes it a recharge | acted as agent true, client received and used true, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised false, exact … | → | treatment recharge, failed conditions separatelyItemised, reason not a disbursement for VAT purposes (separatelyItemised not met): include the cost in the value of your supply and… |
| exactAmountRecovered alone failing makes it a recharge | acted as agent true, client received and used true, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised true, exact a… | → | treatment recharge, failed conditions exactAmountRecovered, reason not a disbursement for VAT purposes (exactAmountRecovered not met): include the cost in the value of your supply… |
| additionalToOwnSupply alone failing makes it a recharge | acted as agent true, client received and used true, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised true, exact a… | → | treatment recharge, failed conditions additionalToOwnSupply, reason not a disbursement for VAT purposes (additionalToOwnSupply not met): include the cost in the value of your supp… |
| the solicitor's own train fare: not received by the client, not additional to the solicitor's supply | acted as agent true, client received and used false, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised true, exact … | → | treatment recharge, failed conditions clientReceivedAndUsed, additionalToOwnSupply, reason not a disbursement for VAT purposes (clientReceivedAndUsed, additionalToOwnSupply not me… |
Show the other 4 tests
| Case | Arguments | Expected | |
|---|---|---|---|
| a bank's telegraphic transfer fee (Notice 700 25.1.3 example 1): supplied to the solicitor, not as agent | acted as agent false, client received and used false, client responsible to pay false, client authorised payment true, client knew third party true, separately itemised true, exac… | → | treatment recharge, failed conditions actedAsAgent, clientReceivedAndUsed, clientResponsibleToPay, reason not a disbursement for VAT purposes (actedAsAgent, clientReceivedAndUsed,… |
| a search fee passed on with a handling mark-up | acted as agent true, client received and used true, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised true, exact a… | → | treatment recharge, failed conditions exactAmountRecovered, reason not a disbursement for VAT purposes (exactAmountRecovered not met): include the cost in the value of your supply… |
| nothing met: every condition is listed, in HMRC's order | acted as agent false, client received and used false, client responsible to pay false, client authorised payment false, client knew third party false, separately itemised false, e… | → | treatment recharge, failed conditions actedAsAgent, clientReceivedAndUsed, clientResponsibleToPay, clientAuthorisedPayment, clientKnewThirdParty, separatelyItemised, exactAmountRe… |
| a condition left unanswered is an error, not a quiet recharge | acted as agent true, client received and used true, client responsible to pay true, client authorised payment true, client knew third party true, separately itemised —, exact amou… | → | error: separatelyItemised must be true or false |
Lint allowances
Before anything is published, Functional Weave reads every implementation and refuses code that reaches outside the function. A few of its rules can be waived on one line, with a reason. This version is published despite one such finding. A registry reviewer read each one its publisher annotated (fune-allow) before it went out; one marked reviewed was accepted by the registry’s maintainers. Read the lines before you build it in.
| Where | Rule | Reason | Allowed by |
|---|---|---|---|
| impl/python.py:22 | dynamic-attribute | reads the eight dataclass fields named in the constant _CONDITIONS; no name comes from input getattr() with a computed attribute name can reach any attribute (__class__, __globals__, ...); name the attribute (obj.field), or annotate it with fune-allow dynamic-attribute and the reason |
reviewed by the registry |
More from the author
1. `actedAsAgent`: you acted as the agent of your client when you paid the third party; 2. `clientReceivedAndUsed`: your client actually received and used the goods or services provided by the third party (this usually rules out your own travel and subsistence, phone bills and postage); 3. `clientResponsibleToPay`: your client was responsible for paying the third party (for example Stamp Duty on the client's contract); 4. `clientAuthorisedPayment`: your client authorised you to make the payment on their behalf; 5. `clientKnewThirdParty`: your client knew that the goods or services would be provided by a third party; 6. `separatelyItemised`: your outlay is separately itemised when you invoice your client; 7. `exactAmountRecovered`: you recover only the exact amount you paid to the third party; 8. `additionalToOwnSupply`: the goods or services are clearly additional to the supplies you make to your client on your own account.
This capability applies that checklist and nothing more. It returns the treatment, the names of any conditions that failed (in the order above) and a one-sentence reason. Deciding each fact is the professional judgement: HMRC's own examples in section 25.1.3 include a bank's telegraphic transfer fee, which is supplied to the solicitor, not the client, and so is a recharge even at cost.
On the invoice, a recharge goes into "value for VAT" with your fees, and a disbursement goes below the VAT line (Notice 700 section 25.1.2 has the worked example: services 80.00 + expenses 20.00 = value for VAT 100.00, VAT 20.00, disbursements 50.00, total 170.00). If you treat a standard-rated cost as a disbursement you cannot reclaim its input tax, because the supply was made to your client, not to you. Compute the VAT itself with finance.tax.add-vat.
Source: HMRC, "VAT guide (VAT Notice 700)", section 25 "Supplies made by or through agents: other situations", 25.1 "Disbursements for VAT purposes", https://www.gov.uk/guidance/vat-guide-notice-700, read 23 September 2026. See also "VAT: costs or disbursements passed to customers", https://www.gov.uk/guidance/vat-costs-or-disbursements-passed-to-customers. UK VAT only.
## Before you rely on this
**Not professional advice.** This capability calculates legal figures from published rules. It is a software component for developers, not legal advice. Rules change and every rate here has an effective date. Check that the dates cover your case. Verify results against the official sources listed above, and have a solicitor review how you use it, before anyone relies on the output. Provided "as is" under its licence, without warranty.
**Unreviewed.** This capability's implementations agree in every language and pass its published test vectors, which were worked out from the official sources cited. But no qualified solicitor has yet checked those vectors, or confirmed that the capability covers the cases it claims. Treat it as a draft. Do not use it for real people, money or decisions without your own expert review. Once a qualified reviewer signs off, this notice is replaced with their name, qualification and the date. Each new version needs fresh sign-off.
1.0.1 marks it unreviewed. The code and the tests are unchanged.
Files
| Path | Bytes |
|---|---|
| README.md | 3,865 |
| impl/python.py | 1,719 |
| impl/rust.rs | 2,881 |
| impl/typescript.ts | 1,417 |
| vectors.json | 9,544 |