The capture problem - an ask is not an authorization
A user request arrives as language. An authorization has to leave as a constrained object. Everything missing between the two has to come from somewhere, and every source is a risk: inferred from context, defaulted by the platform, filled in by the model, or asked of the user.
Take the Munich hotel. The request specifies a city and a night. It does not specify a price ceiling, a currency, a property, a cancellation policy, a room class, or how long the authorization should remain usable if the agent cannot book immediately. A naive capture layer resolves all of that silently and presents the user with a single number to approve. That works right up until the inference is wrong, at which point the user has cryptographically signed a purchase they never conceived of.
It helps to separate two things the word "intent" is doing. There is the task intent — what the user wants to accomplish, which is a planning input for the agent and carries no authority. And there is the intent mandate — the signed envelope that says money may move under these constraints. The agent's plan can be as open-ended as it likes. The mandate cannot. Conflating them is how "find me a good hotel" turns into an envelope big enough to buy a suite.
The capture layer's job is the translation: decide which fields must be pinned before a signature is meaningful, which may be left open for a later resolution step, and which the user has to see and confirm rather than have chosen for them.
Under-specification - what the agent may infer, and what it must ask
Inference policy is a product decision with a security blast radius, and it is worth writing down explicitly rather than letting it emerge from prompt behaviour. A workable split is three tiers.
Never inferred
The amount ceiling and the payee scope. These are the two fields that decide how much damage a compromised or confused agent can do, and both must trace to something the user saw. A ceiling derived from "what is in the account" or a default the platform picked is not consent, it is a limit the user never evaluated. Same for the class of merchant: "any travel merchant" and "this specific hotel" are different grants and the user has to know which one they made.
Inferred, but disclosed
Dates, quantities, room or fare class, and delivery address pulled from conversation history or a saved profile. Inference here is what makes agents useful — re-asking for the travel dates the user gave two turns ago is how you train people to stop reading. The rule is that anything inferred appears on the confirmation surface as an inferred value, visibly attributed, so a wrong pickup is caught by reading rather than by receiving the wrong booking.
Inferred silently
Presentation-level facts with no authorization consequence: display locale, whether prices are shown tax-inclusive, sort order, timezone for a displayed check-in time. These do not change what is authorized.
The failure modes sit at both ends. Infer too little and every purchase becomes a multi-question interrogation, which produces abandonment and, worse, teaches users to approve without reading. Infer too much and the intent quietly acquires constraints nobody chose. The tell for the second is an intent whose ceiling is a round number: round numbers are almost always a default, because real prices are not round.
Deriving the ceiling from the plan, not from the balance
The amount ceiling is the single most load-bearing number in the object, and the most casually chosen. The correct derivation starts from the agent's actual plan and adds only the deltas that plan cannot predict.
Concretely, for a hotel quoted at 182.00 EUR per night: the quoted base is the anchor. City tax and VAT may be quoted separately and are knowable at capture time if the agent read the rate breakdown — add them as line items rather than as slack. What genuinely cannot be pinned is the drift between capture and execution: the merchant re-pricing between quote and booking, an FX conversion if the funding instrument is not EUR, and any fee the merchant adds at the final step. That residual is what the buffer is for.
So the ceiling is quoted total + known taxes and fees + a named buffer, and the buffer
should be a policy value with a stated reason, not a habit. A two to three percent buffer covers ordinary
FX and rounding drift on a same-currency or major-pair booking. It does not cover a merchant doubling the
price, and it should not try to — that case is supposed to fail.
Both directions of error are expensive, and asymmetrically so. A buffer that is too tight means a 3-EUR resort fee pushes the cart past the ceiling, authorization fails, and the agent must wake a user who is asleep in another timezone — the booking is lost to a rounding error. A buffer that is too loose means the envelope is materially larger than the purchase, and every downstream control is operating against a limit that no longer reflects what the user agreed to. The second failure is worse because it is silent: nothing rejects an over-broad ceiling, and the transaction that exploits it verifies cleanly.
A useful discipline is to record the derivation alongside the intent — base, taxes, buffer, and the policy that chose the buffer — so that a ceiling can later be explained rather than merely observed. Tracking cumulative draws against the envelope once it is live is a different problem with a different owner; see the mandate article on who holds the running total.
Open-ended intents - scoping before a merchant exists
A large class of agent tasks cannot name a payee at capture time. "Find me a flight to Munich under 400 EUR next Tuesday" authorizes a purchase from a merchant that has not been chosen yet, because choosing it is the task. The intent is created before discovery, not after.
This is exactly why AP2 separates the envelope from the instance: the intent mandate can be signed with a merchant scope rather than a merchant identity, and the concrete counterparty is fixed later by the cart that draws against it. The relationship between the two objects is covered in the mandate article; what matters at capture time is what you put in the scope field when you cannot put a name there.
The substitutes, in decreasing order of tightness: an explicit allowlist of candidate merchants the agent surfaced during a preliminary search, which is tight but forces discovery to happen before consent; a merchant category, which is the common choice and is only as good as the taxonomy behind it; and a network- or platform-level constraint such as "merchants onboarded to this payment provider", which is weak and should be paired with a low ceiling and a short window.
Category scoping deserves suspicion. Category taxonomies were built for interchange pricing and fraud statistics, not for consent, and their granularity is uneven — some categories are narrow enough to be a real constraint, others cover an enormous range of merchants. A category that the user would describe in one word is usually fine; a category that requires a sentence to explain is not a boundary.
The compensating control for an open scope is time and money, not identity. If you cannot say who, say how much and for how long: a tight ceiling and a validity window measured in hours, so an under-specified payee is bounded by an envelope that cannot be drawn against tomorrow.
What you see is what you sign
The user reviews a rendering. The signature covers a payload. If those two diverge, the system has a consent bug with no cryptographic symptom — verification passes at every downstream step, because the bytes are exactly what was signed. They just are not what the human read.
This is the consent-surface twin of a problem the mandate article names on the crypto side, where signing a summary instead of the canonical object lets fields outside the signature be altered. Here the bytes are fine and the screen is wrong, which is harder to detect because there is nothing to verify against.
The structural fix is to render from the payload rather than beside it. The confirmation surface should be a deterministic function of the exact object about to be signed, built after serialization, so there is no path by which a display value and a signed value are computed from different sources. Any field that cannot be rendered should block the signature rather than be omitted from the screen — silent omission is how an unreviewed constraint gets consented to.
Render the constraints, not just the purchase. Users are shown a price and a merchant; they are far less often shown the ceiling, the scope and the expiry, which are the parts that actually describe the authority being granted. "182.00 EUR to Hotel Munich" and "up to 400 EUR at any travel merchant for the next 30 days" can be the same object, and only one of those sentences tells the user what they are signing.
The mundane traps are formatting. A minor-unit amount rendered as a major-unit one is a hundred-fold error. A currency symbol that is ambiguous across locales — the same glyph used by several dollar currencies — turns a scope check into a guess. Render currency as an explicit code next to the amount, and derive the decimal placement from the currency's own minor-unit exponent rather than assuming two.
Consent granularity and the fatigue curve
Per-transaction approval is the safest design and the least usable one, and beyond a certain frequency it stops being safe too. A user asked to approve every purchase does not read the fourth one. Rubber stamping is not a user failing; it is the predictable output of a system that asks too often, and it converts an explicit control into a reflex.
The alternative is a pre-authorized envelope: one reviewed grant that the agent draws against several times. That trades a reviewed decision per transaction for a reviewed decision per envelope, and moves the risk into how well the envelope was scoped — which is the whole subject of this article.
The useful framing is a threshold function over three inputs. Amount, relative to the user's own baseline rather than an absolute figure, since a 200-EUR purchase is routine for one account and anomalous for another. Novelty — a first-time payee is categorically different from one with an established history, and is where authorized-push-payment style losses concentrate. Reversibility, which is a property of the execution path: a purchase that can be refunded tolerates a lower consent bar than one that cannot, and the rails differ enormously here (see AP2 payment rails).
Rate-based controls are a separate and complementary layer, covered in velocity limits. The capture-layer point is narrower: prompt because a specific input crossed a threshold, and say which one on the prompt. "This is a merchant you have not paid before" is a prompt a user reads. An undifferentiated confirmation dialog on every transaction is a prompt they dismiss.
Poisoned inputs make well-formed intents
The content of an intent is authored by a model that has been reading untrusted web pages. That is a capture-quality problem, and it is genuinely distinct from the ones cryptography addresses: a poisoned observation produces an intent that is honestly captured, correctly rendered, legitimately signed, and wrong. Every verifier downstream will accept it, because there is nothing malformed about it.
The threat model itself — injection as an adversary class, and the containment argument for scopes and ceilings — belongs to AP2 security. What the capture layer owes is provenance: for each field the model did not get from the user, a record of where it came from.
Practically that means the intent carries, or references, the evidence that justified it: the page or API response the price was read from, the merchant identity as resolved rather than as advertised, and a timestamp for each. Store evidence by reference and digest rather than embedding it — screenshots and page captures are large, frequently contain unrelated personal data, and have a different retention requirement from the authorization itself.
Provenance does not prevent a poisoned intent. It makes one explicable afterwards, which is the difference between a dispute you can reconstruct and one where the only available statement is that the signature was valid.