Inspiration

An AI shopping agent reads the price on the product page. For a cross-border order that is simply not the price that lands.

Tell an agent to buy 60 tonnes of hot-rolled coil under EUR 45,000. It finds a listing at EUR 40,000, it pays, and the importer is later billed EUR 10,090 it never saw. Import VAT, the EUR 150 customs duty relief threshold and, for six categories of goods entering the EU, a carbon border (CBAM) certificate cost all attach at the border. None of them is on the product page.

The agent did not get the price slightly wrong. It was working from a number that was never the price. And it had no way to know, because the only surface it could read was the listing.

What it does

Landed is the call that is missing from that transaction. Give it the order and it returns the regulatory lines with the legal instrument each one comes from, then builds the PayPal Orders v2 order with those lines itemised and the citation inside each line description, so the approval screen carries the law and not just the number.

The worked case in the live demo: EUR 40,000 of hot-rolled coil from India into the EU lands at EUR 51,290. CBAM certificates EUR 228.00, customs duty EUR 1,000.00, import VAT at 21% EUR 8,862.00, freight EUR 1,200.00. The regulatory cost alone is EUR 10,090, a 25.2% uplift on the price the agent would have quoted.

The part that matters: it refuses to guess.

Every landed-cost tool on the market returns a number. The useful question is whether that number was derived or estimated, and almost none of them tell you.

A customs duty rate is specific to the tariff line and the origin. Landed does not hold the tariff, so it will not assert one. The line comes back amount_eur: null with what it needs and where to read it. And because duty sits inside the taxable amount for import VAT, an unresolved duty makes the VAT line unresolved too, rather than quietly returning a VAT figure understated by the duty times the rate. The quote reports complete: false, and the PayPal order excludes those lines from the amount charged instead of estimating them into it: EUR 41,428.00, with tax_total at 0.00.

A line that does not apply is absent, not zero. Goods outside CBAM Annex I return no CBAM line at all, because a zero reads as "computed, nothing due" and absence reads as "this instrument does not reach this order", which is the true statement.

So the answer is always one of exactly three things: here is the figure and here is the instrument, this instrument does not reach your order, or I need this one input and here is where to read it. Never a number with nothing behind it. That is the whole design, because an agent that cannot tell a derived number from an estimate cannot responsibly spend someone else's money.

How we built it

A deterministic calculation engine in plain Node with no dependencies, wrapped in three surfaces:

  • PayPal Orders v2, create and capture, OAuth2 client credentials, sandbox by default. Each regulatory line is its own order item with its legal instrument in the line description. The breakdown is honest: item_total is the goods plus the non-VAT regulatory lines, tax_total is the import VAT, shipping is the freight, and the three add to the total to the cent, because PayPal rejects the order otherwise.
  • An MCP server over stdio, exposing landed_quote, landed_paypal_order (with dry_run to see the exact order body without contacting PayPal) and landed_capture_order. This is the AI integration and it is the point of the project: the consumer of Landed is an agent, not a person. claude mcp add landed -- node /absolute/path/to/landed/src/mcp.js and any MCP-capable agent can price the border before it pays.
  • An HTTP API and a hosted demo on Cloudflare Pages Functions, running the identical engine.

What it computes, and from what:

Line Instrument
CBAM certificate cost Regulation (EU) 2023/956 as amended by Regulation (EU) 2025/2083; phase-out factor from Directive (EU) 2023/959 Art 10a(1a)
Customs duty relief Council Regulation (EC) No 1186/2009 Art 23
Import VAT Council Directive 2006/112/EC Arts 2(1)(d), 96, 97; Directives (EU) 2017/2455 and 2019/1995
EU AI Act obligations Regulation (EU) 2024/1689

The CBAM and AI Act engines are not new code written for a hackathon. They are carried over unchanged from a production API at bindler.co/api, where they are recalculated cell by cell against an independent replica before release. What was built during the submission period is the agentic commerce layer: the PayPal Orders v2 integration, the unresolved-line contract, the MCP server, the hosted demo and the test suite.

Challenges we ran into

Making "I do not know" a first-class return value. The easy version returns a VAT estimate and a disclaimer. That is worse than useless to an agent: it cannot distinguish the disclaimer from the number, so it acts on the number. Threading unresolvedness through the engine, into the quote, and all the way into the PayPal amount (excluded, not estimated) was most of the work, and it is the reason an incomplete quote can never be turned into money.

The dependency between lines. Import VAT looked independent until you notice that customs duty sits inside its taxable amount. An unresolved duty therefore has to invalidate the VAT line, or the engine returns a VAT figure that is wrong by exactly the duty times the rate, and nobody finds out until the goods are at the border.

Keeping the PayPal breakdown to the cent. PayPal rejects an order whose breakdown does not sum to the total, so the rounding has to be decided once, at the line, and never re-derived.

Accomplishments that we're proud of

node test/verify.js runs 49 checks and every expected figure in it was worked out by hand from the instrument and written down before the engine was called, because a test that recomputes the answer the same way the code does is just the code twice. For example: 60 t of steel at 1.9 tCO2e embedded per tonne is 114.0 tCO2e; the 2026 CBAM factor is 0.025, so 2.85 tCO2e is chargeable; at EUR 80 a certificate and no origin carbon price that is EUR 228.00, and 3 whole certificates are surrendered.

It also proves the things that break quietly: the origin carbon price credit floors at zero rather than paying a refund, the PayPal breakdown adds to the total to the cent, and an incomplete quote is never turned into money.

What we learned

That the gap in agentic commerce is not arithmetic, it is provenance. An agent transacting across a border has to answer a question no product page answers, and it has to answer it before the money moves. Today it either ignores the border, which makes every cross-border quote wrong, or it asks a human, which is the thing it was deployed to avoid. The useful primitive is not a better estimate. It is a number that arrives with its source attached, and a refusal that arrives with its missing input named.

What's next for Landed

Reading the duty rate from TARIC so the one unresolved line resolves itself; more destinations beyond the EU; and the same unresolved-line contract applied to the other costs an agent cannot see on a page.

AI tool identification

The AI integration is the Model Context Protocol server: Landed is built to be called by an AI agent, and landed_quote, landed_paypal_order and landed_capture_order are exposed as MCP tools so any MCP-capable agent (Claude, or any other client) can price the border inside its own reasoning loop before it authorises a payment.

Landed deliberately does not call a language model to produce any figure. Every number it returns is computed deterministically from the cited instrument, and any number it cannot derive is returned as unresolved rather than generated. That is the point of the project: a figure an agent can act on has to be traceable, and a model-generated figure is not.

Claude (Anthropic) was used as a coding assistant while building the project.

PayPal integration summary

  • Orders v2 create and capture, OAuth2 client credentials, sandbox environment by default.
  • Every regulatory line is its own order item, with its legal instrument in the line description, so the citation travels with the money to the approval screen.
  • item_total + tax_total + shipping reconcile to the order total to the cent.
  • Unresolved lines are excluded from the amount charged rather than estimated into it.
  • dry_run returns the exact order body without contacting PayPal, so the whole flow is inspectable without credentials. Nothing is written to disk and no credential is ever logged.

Built With

Share this project:

Updates

Submission history