A human shopper reads a price tag and quietly does a dozen corrections before deciding: that’s before tax, shipping is probably a tenner, the big pack is cheaper per unit, and that ‘from’ price is for the model I don’t want. An agent shopping under an AP2 mandate has to do the same corrections explicitly, on data of uneven quality — and then commit real money to the answer. The gap between the number a merchant advertises and the number that leaves the user’s account is where agentic commerce goes wrong: ranking merchants on headline price is not a comparison at all, and a purchase authorized against a stale quote is a purchase the user never agreed to. This piece walks the problem end to end.
A price is not a number, it is a resolved total
The first discipline is refusing to treat ‘the price’ as a scalar. What an agent needs before it can commit is a resolved payable total: one amount, in one currency, for one specific item configuration, delivered to one address, paid with one instrument, valid at one moment in time. Every one of those qualifiers can move the number.
That reframing changes what a pricing signal has to carry. A useful signal is not 19.99; it is a value plus a currency code, a unit of measure, an identifier for the exact variant it applies to, a statement of what is included, and a validity window. Anything less is an advertisement — useful for shortlisting, useless for authorizing. Agents get into trouble by letting an advertisement flow straight into a decision, because at no point did any system claim the advertised figure was the payable one. Treat resolution to a payable total as a mandatory step before the agent spends anything.
Structured feeds versus reading the rendered page
Prices reach an agent by one of two very different routes. The good route is structured: a merchant product feed, an in-page machine-readable annotation, or a commerce API that returns typed fields — amount as a minor-unit integer, an ISO 4217 currency code, an availability state, a variant identifier. The fields have declared meanings, so the agent parses rather than guesses.
The bad route is scraping a rendered page. It looks workable in a demo and fails constantly in production. The page renders client-side, so the number appears only after scripts run. Three numbers sit near each other — list price, sale price, monthly financing — and the largest font is not always the payable one. A markup change silently converts a reliable extractor into a confident liar, which is worse than a broken one. Where a structured source exists, prefer it absolutely; where you must scrape, treat the result as low-confidence input to be confirmed against a checkout quote before money moves.
The components that make a headline price wrong
Even a perfectly extracted headline is usually incomplete. The gap between it and the landed total is made of predictable parts, and an agent should model each one explicitly rather than folding them into a fudge factor:
| Component | Why it moves the total |
|---|---|
| Tax | VAT/GST markets quote tax-inclusive; US sales tax is added at checkout and depends on the destination |
| Shipping | Varies by weight, speed, destination, and free-shipping thresholds |
| Duties | Cross-border orders may add duty and handling, sometimes billed on delivery |
| Fees | Handling, small-order, recycling levies, booking fees, deposits |
| Payment method | Some instruments carry surcharges or forfeit a discount tied to another method |
| Currency | A foreign-currency price is not yet a number in the user’s currency |
Two properties matter more than the list. First, several components depend on the destination, so a total is only meaningful once a shipping address is known. Second, some are non-linear: crossing a free-shipping threshold can make a larger basket cheaper than a smaller one, which no per-item comparison will surface.
Freshness: the race between the quote and the purchase
Every price an agent holds is a snapshot with a shelf life. Between reading it and paying it, a promotion can end, stock can run out, a dynamic-pricing engine can re-rate, or a foreign-exchange quote can expire. The interval is small for a human — seconds between clicking and paying — but agents plan. An agent that gathers candidates, reasons over them, waits for a user confirmation, and then purchases may be acting on data hours old.
Make freshness a first-class property. Every captured price carries the timestamp it was observed and, where the merchant provides one, a validity window or quote identifier. Discovery-stage prices are indicative only. Before committing, the agent takes a binding quote — the merchant’s statement of the total it will honour for a bounded period — and pays against that. If the binding quote differs from the indicative one by more than an agreed tolerance, that is not noise to absorb; it is a re-decision, because the comparison that produced the choice may no longer hold.
Personalized and dynamic pricing, and how an agent detects it
The comfortable assumption that a product has a price is often false. Dynamic pricing re-rates by demand, inventory, or time of day. Personalized pricing goes further and varies by who is asking: geography, device, loyalty tier, session history, or an inferred willingness to pay. An agent that samples once and caches the result may be reasoning about a price that exists only for it.
Detection is empirical. The agent varies one identity dimension at a time and watches whether the number moves — a clean session with no cookies against a warm one, a different declared delivery region, a different device profile — and re-samples the same item over time to separate dynamic variation (moves for everyone) from personalized variation (moves per requester). Where the spread is wide, a single observation is a poor basis for a commitment, and the honest response is to re-quote at the moment of purchase rather than present a precision the data does not support.
Bundling and unit-price normalization
Comparison only works between comparable things, and merchants are not obliged to make things comparable. The same coffee appears as 250 g, 500 g, a three-pack, and a subscription. The same software appears as a monthly seat, an annual seat billed up front, and a tier that bundles support. Ranking those by headline price ranks packaging, not value.
The remedy is normalization to a common denominator before any comparison: cost per gram, per litre, per sheet, per seat-month, per unit of the thing the user actually wants. Two cautions attach. First, normalization must respect the user’s real need — the cheapest cost per gram is a bad recommendation if it means five kilos of something perishable, so quantity constraints belong in the intent. Second, bundles resist normalization by design: a package containing an item the user did not ask for is not automatically better value, and an agent should price it against the components the user actually wanted.
A worked comparison: the ranking inverts
The point of all this bookkeeping is that it changes answers. Take an illustrative shortlist for one item, shipped to one address, with figures chosen only to show the mechanism:
| Merchant | Headline | Shipping | Tax | Fees | Landed total |
|---|---|---|---|---|---|
| A | 89.00 | 12.00 | 8.90 | 3.50 | 113.40 |
| B | 94.00 | 0.00 | 9.40 | 0.00 | 103.40 |
| C | 91.50 | 6.00 | 9.15 | 0.00 | 106.65 |
On the headline, the ranking is A, then C, then B. On the landed total it is exactly reversed: B, then C, then A. The merchant that looked cheapest is the most expensive, and the difference is not marginal — it is about ten percent, entirely inside the components a headline comparison discards. This is the ordinary case, because free shipping and inclusive pricing are competitive tactics that deliberately shift cost between the visible and invisible parts of the total. An agent that reports ‘cheapest’ without saying cheapest by what measure is reporting an artefact of presentation.
The adversarial merchant
So far the difficulty has been accidental. Now assume a merchant that would rather an agent believed a favourable number. The incentive is obvious: if agents shortlist on headline price, then whatever the agent reads first is the entire competition, and the cheapest way to win it is to move cost somewhere the agent does not look.
The techniques are the familiar dark patterns of consumer retail, ported to a machine audience: drip pricing, where mandatory fees appear only at the last checkout step; a headline that quietly applies to a lower variant or a smaller quantity; a feed that stays optimistic while the live checkout does not; availability claimed for a back-ordered item. The defences are structural rather than clever. Never rank on a number you have not resolved to a landed total. Re-verify at checkout and treat the delta between advertised and quoted as a measured signal about that merchant, not a rounding error. Keep that history: a merchant repeatedly wrong in its own favour should stop being shortlisted.
Binding the verified price into a cart mandate
All of this is preparation for one moment: the user authorizes a purchase. AP2’s value here is that authorization attaches to a specific, verified total rather than to a vague instruction to go buy something. The cart the user signs carries the resolved amount and its breakdown — item, shipping, tax, fees, currency — so the number they approve is the number that will be charged.
That makes drift a protocol event rather than an inconvenience. If the final charge does not match the authorized total, the mismatch is detectable by construction, and the agent’s options are defined in advance: proceed if the difference falls inside a tolerance the user explicitly granted, otherwise stop and re-confirm. Setting that tolerance is a real design decision — too tight and ordinary tax rounding aborts good purchases, too loose and it is a blank cheque — and it is one the user, not the agent, should own.
Keep the working, too: the candidate prices with their sources and timestamps, the breakdown that turned each headline into a landed total, and the binding quote. That record answers ‘why this merchant?’ with arithmetic rather than assertion, and it is the evidence you want if the charge is ever disputed.