UCP Checker
When the Total Isn't Final: UCP Proposals Add a Buyer-Approved Maximum and Split Authorization From Capture

When the Total Isn't Final: UCP Proposals Add a Buyer-Approved Maximum and Split Authorization From Capture

An agent asks a buyer to approve $54.00. The store weighs the produce, splits the shipment, adds a hotel incidental — and charges $57.80. Was that allowed? Today UCP has no way to say. A Checkout total is just a number, the protocol doesn't record whether it can move after the buyer says yes, and the resulting Order doesn't record what was actually authorized or taken.

Three linked pieces of work filed this week, all authored by Stripe, would change that. None is merged — all three are still open — but together they are the most substantial change to UCP's core payment model since the payment layer started filling in over the summer, and the core piece is marked as a breaking change.

What was filed

Date Item What it does Status (2026-09-10)
2026-09-09 #815 — Enhancement Proposal Final vs provisional Checkout totals, a buyer-approved maximum, authorization and capture summaries on the Order Open proposal
2026-09-09 #816 — feat!: define checkout payment finality and order reporting The schema and spec changes for #815 (16 files, +394/−60) Open PR, Core Protocol — requires Technical Council approval
2026-09-10 #820 — Enhancement Proposal An optional extension for capture policy and a provider processing log on the Order Open proposal; namespace proposed, not approved

They sit on top of an earlier PR, #584, opened in July to put payment information on the Order at all — so confirmation screens can show a split between a gift card and a card. That PR carried a single amount per instrument. Stripe's comment on it on 2026-09-01 is the clearest statement of why this week's work exists: a single amount "commits the protocol to 'amount charged' semantics before auth or confirmed payment." #816 replaces it with two fields, and builds on #584 rather than competing with it.

Final or provisional

The core change is one new field on one entry. The aggregate Checkout total — the single totals[] entry with type: total — may carry amount_finality, either final or provisional. Omit it and the total is final, so every Checkout in the field today keeps its current meaning. No other total entry may carry the field.

Finality then decides the buyer's ceiling:

  • Final total: the total is the approved maximum. payment.maximum_amount must be omitted.
  • Provisional total: payment.maximum_amount is required, and must be at least the displayed total.

The PR's own example is a $54.00 Checkout that may rise to no more than $60.00 after completion:

"totals": [
  { "type": "subtotal", "amount": 5000 },
  { "type": "tax", "amount": 400 },
  { "type": "total", "amount": 5400, "amount_finality": "provisional" }
],
"payment": {
  "maximum_amount": 6000
}

The behavioural rules are where the agent comes in. Before completing a provisional Checkout, the Platform must tell the buyer the amount may change up to maximum_amount, and obtain their approval. The Business must not authorize or collect more than that, across all instruments, under the approval given at Checkout. Anything higher needs a fresh approval. And because the AP2 mandate signs the Checkout response, a provisional total's finality and ceiling end up inside the signed record of what the buyer agreed to.

That matters for more than groceries. UCP can already express goods sold by measured amount — 1.50 kg in hundredth-of-a-kilogram steps — and an Order that settles at a different amount than was ordered: sale-basis steps (#653) merged in August. What it couldn't express was the payment side of that, and #816 adds an approved maximum to exactly those examples. Lodging deposits, restaurant orders and split shipments all produce totals that legitimately move after the buyer approves. Until now a human shopper absorbed that with a line of small print. An agent acting on someone's behalf needs the number.

Authorization is not capture

The second half of #815 is on the Order, which in the current release (v2026-08-25) has no core payment object at all — only the payment-terms extension adds one. #816 makes payment a required part of every Order, carrying:

  • maximum_amount — what the buyer approved at Checkout. It stays fixed even if the buyer later approves an Order change.
  • instruments[], each of which may report:
    • authorized_amount — authorization currently in effect. Released and expired authorizations are excluded.
    • captured_amount — gross successful capture, before refunds, credits, disputes or reversals. Capture, not settlement or payout.

Three rules in the proposal run against intuition, and are worth knowing before building on them:

  1. A Platform must accept captured_amount greater than authorized_amount. One reports current state, the other total activity; an authorization can be released after capture, and some providers support capturing above it.
  2. A Platform must not derive remaining capture capacity by subtracting the two. Capacity is a provider concept the Core fields deliberately don't carry.
  3. A Platform must not compare maximum_amount against the sum of historical authorizations or captures. Replaced authorizations and retried captures can push those sums past what the buyer approved without anything having gone wrong.

There is a security tightening underneath too. The shared payment-instrument shape is split into a base plus two named variants: a Checkout instrument that carries handler_id and selection state, and an Order instrument that explicitly forbids credentials, handler_id and selected. credential becomes formally request-only, never returned in a response — which is why the PR strips credential tokens out of the existing split-payment response examples.

The extension: capture policy and a processing log

#820 builds on the Core change with an optional extension, proposed as dev.ucp.common.payment.processing. It does two things.

Before completion, it lets the parties agree when payment is taken. Businesses and Platforms advertise supported capture modes (automatic or manual) and sequences (single or multiple), with an optional max_captures. The Platform may request a policy; the Business returns the one that will apply on every Checkout response, and it is frozen at completion. For an agent, that's the difference between "you'll be charged now" and "you'll be charged when it ships" — a sentence it can now say from data rather than guess.

After completion, it reports what the payment provider actually did. The design keeps two lists apart that are easy to conflate:

  • approvals[] — commercial approvals the buyer gave: the Checkout approval, then any later Order-change approval, each recording the new ceiling rather than the increase.
  • attempts[] — provider payment lifecycles, each holding append-only entries: authorization, authorization adjustment, capture, capture reversal, release, expiry.

A buyer approving an Order change and a provider approving an authorization are different events with different lifecycles, and the extension doesn't pretend otherwise. The handling of uncertainty is careful as well: when a Business can't tell what happened — a provider request times out — it records a pending entry, and appends the eventual result as a new entry pointing back at it, rather than rewriting history. Older terminal entries may be trimmed, with an entries_complete: false flag so a consumer knows the log is partial.

The proposal is candid about its own governance position: the contribution guide normally expects a new extension to show vendor adoption before entering dev.ucp.*, and it asks the Tech Council for direction on the namespace at the Proposal stage.

What breaks

#816 is marked feat!, and the breaking parts are specific:

  • handler_id leaves the base payment instrument. The root of payment_instrument.json resolves to the shared base, so an external Checkout schema that relied on the root to require handler_id would need to reference selected_payment_instrument instead.
  • Every Order must carry payment.maximum_amount. For a final total, that's simply the Checkout total — but an implementation returning Orders without it would no longer validate.
  • The payment-terms extension's Order payment is no longer marked optional in responses, since Core now requires one on every Order.

The proposal's mitigation is the normal one — the Enhancement Proposal and dated-release process, plus migration examples — and it is a Core Protocol change, which requires Technical Council approval before it can land in any release.

The rule a schema can't check

One sentence in #815 is worth pausing on: JSON Schema cannot compare sibling numeric values. The schema can require a maximum_amount whenever the total is provisional, but it cannot require that the maximum is at least the total. A Checkout declaring a $54.00 total with a $50.00 ceiling passes schema validation. The proposal's answer is layered — Businesses must enforce it, Platforms must reject violations, and conformance tests must cover it — and #820 names the same limit for its approval references, which have to resolve within the same Order.

That's the difference between validating a shape and validating behaviour, and it's the same gap we see across the fleet: what a response declares and what it actually commits to are two separate checks. It's also why the design choice to make Platforms reject bad ceilings matters. In our Playground study of shopping agents under store misbehaviour, one model declined an error body's "override" that would force a payment capture, reasoning to the customer that it "risks a double charge or a charge with no valid order attached." The model got there on judgement. A spec in which the buyer's ceiling, what was authorized and what was captured are explicit, reported fields gives agents something firmer than judgement to check against.

Where this sits

The timing is worth noting: this lands a week after the Payments Tech Council was seated, and on top of an August release that added payment authentication, negotiated terms and split payments. The spec is building the money layer faster than the fleet is declaring it — in our August 2026 census, four verified stores declared a payment token, none of them a production storefront. That's the normal order of events for a standard: the primitives arrive, then the platforms that carry most stores adopt them in cohorts.

What to watch:

  • Whether #584 and #816 are reviewed together. Stripe asked on #584 to coordinate or defer so the designs could be evaluated side by side; #816 is built as a branch on top of it.
  • The Technical Council's call on #815 — the step that would move it to Working Draft.
  • The namespace decision on #820, and whether the adoption expectation is waived or met.
  • Which dated release carries it, if approved — and whether it arrives together with the extension or ahead of it.

When a total can move after the buyer says yes, the approved ceiling is the one number an agent has to get right. UCP is about to decide how to write it down.


About UCP Checker

UCP Checker is the independent validation and observability layer for the Universal Commerce Protocol. We crawl, validate and grade every public UCP manifest we can find, run the merchant directory, the UCP Score, live adoption stats, the vertical guides and the authority-bound vendor map, and track the spec as it evolves so you don't have to — measured from two vantages (what a business declares, and what actually happens when an agent transacts), the same way for everyone, without picking winners.

Sources

Check your domain's UCP status

See if your storefront is ready for agentic commerce in seconds.

Weekly UCP Report

Get the agentic commerce digest every Monday

Real adoption data, ecosystem trends, new spec versions, and the stores that broke or recovered this week. Read by founders and engineers building the next generation of commerce.

Free forever No spam Unsubscribe anytime

View a sample report →

Weekly UCP Report
Issue #53 · Oct 5, 2026
+664
new verified stores
Verified rate
Latest spec
Cart capability