From db4c20462922da79f99f955c47a3f0cd81c9baa1 Mon Sep 17 00:00:00 2001
From: rgarcia <72655+rgarcia@users.noreply.github.com>
Date: Fri, 25 Sep 2026 01:58:52 +0000
Subject: [PATCH 1/2] Update Link payment docs for checkout-bound card items
---
browsers/enable-payments-in-browser-agent.mdx | 209 +++++++++++-------
browsers/payments.mdx | 14 +-
integrations/wallets/agentcard.mdx | 102 ++-------
integrations/wallets/overview.mdx | 21 +-
integrations/wallets/stripe-link.mdx | 197 +++++++++++++----
vaults/fill.mdx | 10 +-
vaults/overview.mdx | 4 +-
7 files changed, 327 insertions(+), 230 deletions(-)
diff --git a/browsers/enable-payments-in-browser-agent.mdx b/browsers/enable-payments-in-browser-agent.mdx
index 92072dc2..70ed63e5 100644
--- a/browsers/enable-payments-in-browser-agent.mdx
+++ b/browsers/enable-payments-in-browser-agent.mdx
@@ -12,7 +12,7 @@ verify and approve each purchase before completing checkout.
- have an existing browser agent. this guide adds payment handling, not navigation or reasoning.
- choose a wallet integration from the [wallet overview](/integrations/wallets/overview), then follow its tab below.
- install a KERNEL sdk with the `vaults` resource. set `KERNEL_API_KEY` and `KERNEL_PROJECT_ID` in your controller.
-- use a low-value checkout you control. link is live-only and requires an https merchant origin and verified field selectors. agentcard requires a [native processor adapter](/integrations/wallets/overview#checkout-and-processor-coverage); the processor doesn't need to be stripe.
+- use a low-value checkout you control. link is live-only and requires an https checkout page; checkouts without link pay token support also need verified card field selectors. agentcard requires a [native processor adapter](/integrations/wallets/overview#checkout-and-processor-coverage); the processor doesn't need to be stripe.
- for agentcard, set `AGENTCARD_MODE` in your controller and verify it against the credential's mode. customer-owned configs expose `test_mode` (`true` means sandbox); for KERNEL-managed credentials, confirm the deployment's mode. stop if the mode is unknown or mismatched.
- identify how you'll read the merchant's trusted order record. you need it to confirm a matching paid order. deterministic checkout data can verify purchase details before submission, but without the order record afterward, the result remains indeterminate.
@@ -34,7 +34,7 @@ and authorization state. neither item is the user's underlying wallet or card.
- keep wallet enrollment and provider approval outside the agent and its browser. never put action urls, oauth codes, provider responses, api keys, or browser connection urls in model context.
- - independently verify purchase details in your controller before creating, updating, or authorizing a card item. user confirmation alone doesn't validate an agent's proposal.
+ - independently verify purchase details in your controller before creating a card item. user confirmation alone doesn't validate an agent's proposal.
- submit checkout once through the merchant's normal form. don't retry submission, automatically retry `fill`, or fall back from `fill` to aliases after failure or uncertainty.
- report success only after the merchant's trusted order record confirms the matching paid order. a timeout, missing event, or browser deletion doesn't cancel a payment.
@@ -177,15 +177,16 @@ connect your existing agent to `browser.cdp_ws_url`. see [Controlling a Browser]
navigate to checkout in this session before verifying the purchase. use the same
session to inspect and submit it; the vault attachment includes items created later.
+link cards are bound to this session and the checkout page they're created on.
### 2. Verify and confirm the purchase
1. let the agent propose the merchant, amount, currency, and item or cart contents. treat every proposed value as untrusted.
2. independently obtain the expected values from a trusted source. prefer your order or cart backend. when no backend exists, use deterministic page extraction with fixed selectors or structured page data, not another model response.
3. normalize the values in your controller and compare the proposal with the trusted result. compare the amount in minor currency units and require the merchant, currency, and item or cart contents to match.
-4. stop when any value is missing, cannot be verified, or disagrees. do not create or update a card item and do not invoke authorization.
+4. stop when any value is missing, cannot be verified, or disagrees. do not create a card item.
5. show the independently verified values to the user and wait for explicit confirmation.
-6. freeze that verified, confirmed purchase object. derive the card specification and any authorization request from that same object. do not accept replacement values from the agent after confirmation.
+6. freeze that verified, confirmed purchase object. derive the card specification from that same object. do not accept replacement values from the agent after confirmation.
for stripe payment links, see [checkout-specific notes](#checkout-specific-notes)
for structured response fields and adaptive pricing.
@@ -212,33 +213,74 @@ step 2, a connected wallet in the same vault, and this browser session.
-#### Prepare and authorize the card
+#### Create the card at checkout
1. list the wallet's payment methods and let the user select one.
-2. [create a link card item](/integrations/wallets/stripe-link) named `notebook-order` from the verified purchase and selected method.
-3. require `authorize` in `available_operations`, invoke it with that same purchase object, and present any returned action through your controller.
-4. wait for the card to become `ready`, then require `fill` immediately before use. link cards never expose `state.aliases`.
+2. with the agent on the final checkout page, read the browser's exact current top-level https url. `checkoutURL` / `checkout_url` below is that url, including query and fragment; no playwright page object is required.
+3. [create a link card item](/integrations/wallets/stripe-link#create-a-card-item-at-checkout) named `notebook-order` from the verified purchase, the selected method, `browser.session_id`, and `checkoutURL`. creating the card starts approval.
+4. present the returned `spend_approval` action through your controller, or tell the user to approve a `push_approval` in their link app.
+5. wait for the card to become `ready`, then require `fill` immediately before use. link cards never expose `state.aliases`.
```typescript TypeScript
-const card = await kernel.vaults.items.retrieve("notebook-order", {
+let card = await kernel.vaults.items.upsert("notebook-order", {
id_or_name: vault.id,
- wait: 60,
+ type: "card",
+ spec: {
+ provider: "link",
+ wallet: wallet.key,
+ browser_id: browser.session_id,
+ page_url: checkoutURL,
+ payment_method_id: verifiedPurchase.paymentMethodID,
+ amount: verifiedPurchase.amount,
+ currency: verifiedPurchase.currency,
+ merchant_name: verifiedPurchase.merchantName,
+ context: verifiedPurchase.context,
+ },
});
+if (card.action && "url" in card.action) {
+ await presentProviderAction({
+ userID: authenticatedUser.id,
+ vaultID: vault.id,
+ item: card,
+ });
+}
+card = await kernel.vaults.items.retrieve(card.key, {
+ id_or_name: vault.id,
+ wait: 60,
+});
if (card.type !== "card" || card.state.status !== "ready") {
throw new Error(`payment item is ${card.state.status}`);
}
```
```python Python
-card = kernel.vaults.items.retrieve(
+card = kernel.vaults.items.upsert(
"notebook-order",
id_or_name=vault.id,
- wait=60,
+ type="card",
+ spec={
+ "provider": "link",
+ "wallet": wallet.key,
+ "browser_id": browser.session_id,
+ "page_url": checkout_url,
+ "payment_method_id": verified_purchase.payment_method_id,
+ "amount": verified_purchase.amount,
+ "currency": verified_purchase.currency,
+ "merchant_name": verified_purchase.merchant_name,
+ "context": verified_purchase.context,
+ },
)
+if card.action is not None and hasattr(card.action, "url"):
+ present_provider_action(
+ user_id=authenticated_user.id,
+ vault_id=vault.id,
+ item=card,
+ )
+card = kernel.vaults.items.retrieve(card.key, id_or_name=vault.id, wait=60)
if card.type != "card" or card.state.status != "ready":
raise RuntimeError(f"payment item is {card.state.status}")
```
@@ -249,19 +291,20 @@ kernel vaults items get user-12345 notebook-order --wait 60 -o json
+KERNEL inspects the checkout when you create the card. if the page is a stripe checkout that exposes link pay token WebMCP tools, the card uses a merchant-bound link pay token; otherwise it uses a one-time virtual card. you don't choose the mode, and the card is bound to this browser session and page. a changed purchase or page needs a new card item under a new key. see [how KERNEL pays](/integrations/wallets/stripe-link#how-kernel-pays).
+
-#### Fill the approved fields
+#### Fill the approved credential
+your controller must authorize the destination before disclosure. `browser_id` and
+`page_url` must match the card's `spec.browser_id` and `spec.page_url` exactly.
-your controller must authorize the destination and selectors before disclosure.
-`checkoutURL` / `checkout_url` is the browser's exact current top-level https url,
-including query and fragment. it must share the origin of `spec.merchant_url`.
-no playwright page object is required. see [field requirements](/integrations/wallets/stripe-link#fill-the-checkout).
+read the `fill` operation's `description` in `available_operations`; it tells you
+which inputs this card needs:
-`fill` supports stored [billing fields](/integrations/wallets/stripe-link#map-card-fields-to-inputs)
-as well as card fields. requesting an absent value returns `field_unavailable`
-before any writes.
+- **the description says to omit fields** (link pay token): send only `browser_id` and `page_url`. KERNEL supplies the token to the checkout's WebMCP tool, and the result's `fields` array is empty.
+- **the description asks for field bindings** (virtual card): also send `fields` for the page's card inputs. `fill` supports stored [billing fields](/integrations/wallets/stripe-link#map-card-fields-to-inputs) as well as card fields; requesting an absent value returns `field_unavailable` before any writes.
@@ -269,73 +312,82 @@ before any writes.
const linkCard = await kernel.vaults.items.retrieve(card.key, {
id_or_name: vault.id,
});
-if (
- linkCard.type !== "card" ||
- linkCard.spec.provider !== "link" ||
- !linkCard.available_operations.some((operation) => operation.type === "fill")
-) {
+const fillOperation = linkCard.available_operations.find(
+ (operation) => operation.type === "fill",
+);
+if (linkCard.type !== "card" || linkCard.spec.provider !== "link" || !fillOperation) {
throw new Error("fill is unavailable for this card");
}
+// the agent reads the description and proposes bindings, or none when told to
+// omit fields; your controller verifies every proposed selector
+const fields = await proposeFillFields({
+ browser,
+ description: fillOperation.description,
+});
const result = await kernel.vaults.items.performOperation(linkCard.key, {
id_or_name: vault.id,
type: "fill",
browser_id: browser.session_id,
page_url: checkoutURL,
- fields: [
- { field: "number", selector: "#card-number" },
- { field: "expiration", selector: "#expiry", format: "MM/YY" },
- { field: "cvc", selector: "#security-code" },
- ],
+ ...(fields.length > 0 && { fields }),
timeout_ms: 10000,
});
if (result.type !== "fill") throw new Error("unexpected operation response");
console.log(result.status, result.fields);
if (result.status !== "completed") {
- throw new Error("stop and reconcile per-field outcomes; do not retry");
+ throw new Error("stop and reconcile the fill outcome; do not retry");
}
```
```python Python
+from kernel import omit
+
link_card = kernel.vaults.items.retrieve(card.key, id_or_name=vault.id)
-if (
- link_card.type != "card"
- or link_card.spec.provider != "link"
- or not any(operation.type == "fill" for operation in link_card.available_operations)
-):
+fill_operation = next(
+ (operation for operation in link_card.available_operations if operation.type == "fill"),
+ None,
+)
+if link_card.type != "card" or link_card.spec.provider != "link" or fill_operation is None:
raise RuntimeError("fill is unavailable for this card")
+# the agent reads the description and proposes bindings, or none when told to
+# omit fields; your controller verifies every proposed selector
+fields = propose_fill_fields(browser=browser, description=fill_operation.description)
result = kernel.vaults.items.perform_operation(
link_card.key,
id_or_name=vault.id,
type="fill",
browser_id=browser.session_id,
page_url=checkout_url,
- fields=[
- {"field": "number", "selector": "#card-number"},
- {"field": "expiration", "selector": "#expiry", "format": "MM/YY"},
- {"field": "cvc", "selector": "#security-code"},
- ],
+ fields=fields or omit,
timeout_ms=10000,
)
if result.type != "fill":
raise RuntimeError("unexpected operation response")
print(result.status, result.fields)
if result.status != "completed":
- raise RuntimeError("stop and reconcile per-field outcomes; do not retry")
+ raise RuntimeError("stop and reconcile the fill outcome; do not retry")
```
+`proposeFillFields` / `propose_fill_fields` represents your agent returning
+bindings such as `{ field: "number", selector: "#card-number" }` for the
+checkout's visible card inputs, including within payment frames. sending
+`fields` to a link pay token card, or omitting them for a virtual card, returns
+`400 invalid_request` before any write.
+
retain each result's zero-based `index`, `status`, and optional `error_code`.
`failed` may leave earlier writes in place; `unknown` or a transport error means
the outcome is uncertain. stop without retrying or switching to aliases. see the
[`fill` outcome contract](/vaults/fill#handle-the-outcome) for details and [link checkout](/integrations/wallets/stripe-link#fill-the-checkout)
for the cli equivalent.
-only continue on `completed`. inspect the checkout without reading card values
-back, complete the remaining customer fields and disclosures, and submit once.
-then [verify the outcome](#verify-the-outcome). filling is not payment success
-and doesn't consume the item or clear its encrypted material; the provider's
-single-use card semantics are separate from item state.
+only continue on `completed`. `fill` never clicks pay. inspect the checkout
+without reading card values back, complete the remaining customer fields and
+disclosures, and have the agent click the checkout's pay button once. then
+[verify the outcome](#verify-the-outcome). filling is not payment success and
+doesn't consume the item or clear its encrypted material; the provider's one-use
+credential semantics are separate from item state.
@@ -346,10 +398,11 @@ aliases are placeholder card values resolved during provider handoff. the agent
enters them instead of real card details, which stay outside the browser.
[create an agentcard item](/integrations/wallets/agentcard) named `notebook-order`
-from the verified purchase, or reuse a ready item and update its specification
-when the api permits. pin an enrolled card with `card_id`, or let the user choose
-during approval. don't invoke `authorize`: authorization starts after the browser
-submits a recognized processor request containing aliases.
+from the verified purchase, or reuse a ready item whose specification already
+matches it. card specifications are immutable; a different purchase needs a new
+item. pin an enrolled card with `card_id`, or let the user choose during
+approval. authorization starts after the browser submits a recognized processor
+request containing aliases.
retrieve the item and require `ready` before reading aliases:
@@ -712,33 +765,37 @@ card.
if adaptive pricing is active, use the checkout's active presentment amount
and currency rather than its base integration values.
if any value is missing, cannot be verified, or disagrees, stop without
- creating or authorizing a card.
+ creating a card.
6. show me the verified merchant, amount, currency, and item or cart contents.
wait for my explicit confirmation, then freeze that verified purchase object.
-7. list the wallet's payment methods and ask me which one to use. create a link
- card item named `checkout-card` from that same verified, confirmed object and
- the selected payment method. use the exact checkout url as `merchant_url` and
- include a specific context of at least 100 characters.
-8. retrieve the card item and confirm that `authorize` appears in
- `available_operations`. give me the exact cli command, but do not run it. ask
- me to invoke authorization and complete any provider action from a trusted
- terminal or application outside this agent. after i confirm completion,
- retrieve the item again and require its status to be `ready`. do not print,
- return, or open an action url.
-9. require `fill` in the card's `available_operations`. inspect the checkout's
- inputs and invoke `kernel vaults items invoke user-12345 checkout-card fill`
- with `--params` or `--spec-file`: browser_id is the attached session id,
- page_url is the exact current top-level https url (including query and
- fragment), and fields maps stored field names to unique css selectors.
- the page must share the card's merchant_url origin. combined expiration
- requires format MM/YY or MM/YYYY; timeout_ms is optional. request only needed
- card and billing fields. fill writes actual values into the browser; do not
- read them back, expose them in model context, or assume recordings omit them.
+7. list the wallet's payment methods and ask me which one to use. with the
+ browser on the final checkout page, create a link card item named
+ `checkout-card` from that same verified, confirmed object and the selected
+ payment method. set browser_id to the attached session id and page_url to the
+ exact current top-level https checkout url (including query and fragment),
+ and include a specific context of at least 100 characters. do not pass a
+ merchant account id or try to choose how link pays; KERNEL inspects the
+ checkout and decides. creating the card starts approval. do not print,
+ return, or open the returned action url. ask me to complete the approval
+ from a trusted terminal or application outside this agent.
+8. after i confirm approval, retrieve the item again and require its status to
+ be `ready`. if a creation or retrieval fails, stop and ask me; do not create
+ a second card.
+9. require `fill` in the card's `available_operations` and read its
+ description. invoke `kernel vaults items invoke user-12345 checkout-card fill`
+ with `--params` or `--spec-file`, using the card's browser_id and page_url
+ exactly. if the description says to omit fields, send no fields. otherwise,
+ inspect the checkout's card inputs and map stored field names to unique css
+ selectors; combined expiration requires format MM/YY or MM/YYYY, and
+ timeout_ms is optional. request only needed card and billing fields. fill
+ writes actual values into the browser; do not read them back, expose them in
+ model context, or assume recordings omit them.
10. preserve the value-free fill result and every per-field outcome. continue
only on `completed`; failed or unknown outcomes, transport errors, or missing
required fields mean stop and reconcile. never automatically retry fill or
- fall back to aliases. supply other customer fields only from approved user
- data, complete any agent disclosure truthfully, then submit checkout once.
+ fall back to aliases. fill never submits payment. supply other customer
+ fields only from approved user data, complete any agent disclosure
+ truthfully, then click the checkout's pay button once.
11. inspect the checkout result, fill outcomes, and item events. report success
only when a trusted merchant order record confirms a paid order matching the
frozen merchant, amount, currency, and items. fill or a success page alone
@@ -780,9 +837,9 @@ card.
an agentcard card item named `checkout-card` from that same verified,
confirmed object. include the selected `card_id`, or omit it so i can choose
an enrolled card during approval.
-8. retrieve the card item and confirm its status is `ready`. do not invoke
- `authorize`; agentcard starts authorization only when the attached browser
- submits a recognized processor request containing the aliases.
+8. retrieve the card item and confirm its status is `ready`. agentcard starts
+ authorization only when the attached browser submits a recognized processor
+ request containing the aliases.
9. give me the exact cli observation commands, but do not run them. ask me to
start the trusted approval observer outside this agent. after i confirm it is
running, use only the returned aliases with
diff --git a/browsers/payments.mdx b/browsers/payments.mdx
index eab30059..4a9ec677 100644
--- a/browsers/payments.mdx
+++ b/browsers/payments.mdx
@@ -5,7 +5,7 @@ description: "Let browser agents complete purchases without handling raw payment
your browser agent can complete purchases with KERNEL while your application keeps control of the purchase and user approval. native [wallet integrations](/integrations/wallets/overview) connect the user's payment method to a [vault](/vaults/overview), so your agent works with payment items rather than raw card details.
-KERNEL's [`fill` api](/vaults/fill) safely injects stored values into browser fields without passing them through your application's injection code or the model's context. you provide an item reference, browser, and field selectors; KERNEL writes the values and returns per-field outcomes, not the values. wallet integrations supply payment material and approval; `fill` is a KERNEL operation shared with non-payment vault items.
+KERNEL's [`fill` api](/vaults/fill) safely injects stored values into browser fields without passing them through your application's injection code or the model's context. you provide an item reference, browser, and page, plus field selectors when the item needs them; KERNEL supplies the values and returns outcomes, not the values. wallet integrations supply payment material and approval; `fill` is a KERNEL operation shared with non-payment vault items.
agentcard uses an integration-specific alternative: the agent enters non-sensitive aliases, and KERNEL holds a recognized checkout request for approval before agentcard executes it and KERNEL replays the response. the underlying card stays outside the browser.
@@ -22,9 +22,9 @@ agentcard uses an integration-specific alternative: the agent enters non-sensiti
1. create a vault for the user or task.
2. create a wallet item and send the user through the provider-hosted collection flow.
3. attach the vault when you create the browser session. the attachment also covers items created later in that vault.
-4. create a card item for the verified, user-confirmed purchase. complete the provider's required preparation and approval. link issues a single-use card; agentcard can reuse a card item with separate approval for each checkout.
-5. invoke KERNEL's advertised `fill` operation to inject payment fields without supplying their values, then inspect the outcomes before submitting. for agentcard's alias-based flow, enter aliases and keep a trusted approval observer running while the checkout request is held.
-6. submit checkout once, complete any remaining provider-hosted approval, and reconcile with the merchant's order state. fill or approval alone is not payment success.
+4. create a card item for the verified, user-confirmed purchase and complete the provider's approval. for link, create it at the final checkout page with the browser session and exact page url; KERNEL picks a link pay token or a one-time virtual card. agentcard can reuse a card item with separate approval for each checkout.
+5. invoke KERNEL's advertised `fill` operation to supply the payment credential without handling its values, then inspect the outcomes before submitting. `fill` never clicks pay. for agentcard's alias-based flow, enter aliases and keep a trusted approval observer running while the checkout request is held.
+6. have the agent click pay once, complete any remaining provider-hosted approval, and reconcile with the merchant's order state. fill or approval alone is not payment success.
`wallet` and `card` are vault item types. the wallet supplies the provider connection; the card represents payment material and its authorization state. retrieve the item and check `available_operations` before invoking an operation. don't assume every item supports `fill`.
@@ -34,8 +34,8 @@ flowchart LR
H --> W[wallet item]
W --> C[prepared card item]
C --> F["KERNEL fill api"]
- T[trusted controller: references and selectors] --> F
- F --> B[attached browser fields]
+ T[trusted controller: references, page, and selectors] --> F
+ F --> B[attached browser: card fields or link WebMCP tool]
F --> O[value-free outcomes]
B --> S[merchant form submission]
C -->|agentcard exception| A[non-sensitive aliases]
@@ -46,7 +46,7 @@ flowchart LR
## Verify the purchase
-your application must independently verify the merchant, items, amount, and currency before creating or authorizing a purchase. keep wallet collection and approval urls in trusted user-facing surfaces, not model context. after submission, use the merchant's order record to establish whether the expected purchase succeeded.
+your application must independently verify the merchant, items, amount, and currency before creating a card item for a purchase. keep wallet collection and approval urls in trusted user-facing surfaces, not model context. after submission, use the merchant's order record to establish whether the expected purchase succeeded.
don't retry a failed, timed-out, rejected, or indeterminate payment. a browser error, missing response, completed `fill`, or reusable card item does not prove whether the merchant created an order or money moved. inspect existing outcomes, item events, and merchant state before taking another action.
diff --git a/integrations/wallets/agentcard.mdx b/integrations/wallets/agentcard.mdx
index 9c50e37a..735101d4 100644
--- a/integrations/wallets/agentcard.mdx
+++ b/integrations/wallets/agentcard.mdx
@@ -422,90 +422,19 @@ kernel vaults items get user-12345 notebook-order -o json
### Reuse a card item for a new purchase
-`upsert` can retrieve an identical item, but it cannot replace the purchase
-specification at an existing key. for a later purchase, retrieve the reusable
-agentcard item and use `update` with the complete new specification:
+agentcard card items are immutable. `upsert` with an identical `spec` returns the
+existing item, and a different `spec` at the same key returns `409`; `PATCH`
+isn't supported for cards. a ready item can be reused for another checkout with
+the same `merchant`, `amount`, `currency`, and `card_id`. each checkout still
+requires its own approval.
-
-
-```typescript TypeScript
-let reusableCard = await kernel.vaults.items.retrieve("notebook-order", {
- id_or_name: vault.id,
- wait: 60,
-});
-
-if (
- reusableCard.type !== "card" ||
- reusableCard.spec.provider !== "agentcard" ||
- (reusableCard.state.status !== "requested" &&
- reusableCard.state.status !== "ready")
-) {
- throw new Error(`card cannot be updated from ${reusableCard.state.status}`);
-}
-
-reusableCard = await kernel.vaults.items.update("notebook-order", {
- id_or_name: vault.id,
- spec: {
- provider: "agentcard",
- wallet: wallet.key,
- merchant: "example books",
- amount: 4199,
- currency: "usd",
- },
-});
-```
-
-```python Python
-reusable_card = kernel.vaults.items.retrieve(
- "notebook-order",
- id_or_name=vault.id,
- wait=60,
-)
-
-if (
- reusable_card.type != "card"
- or reusable_card.spec.provider != "agentcard"
- or reusable_card.state.status not in {"requested", "ready"}
-):
- raise RuntimeError(
- f"card cannot be updated from {reusable_card.state.status}"
- )
-
-reusable_card = kernel.vaults.items.update(
- "notebook-order",
- id_or_name=vault.id,
- spec={
- "provider": "agentcard",
- "wallet": wallet.key,
- "merchant": "example books",
- "amount": 4199,
- "currency": "usd",
- },
-)
-```
-
-```bash CLI
-kernel vaults items get user-12345 notebook-order --wait 60 -o json
-kernel vaults cards update user-12345 notebook-order \
- --provider agentcard \
- --spec '{
- "wallet": "agentcard-wallet",
- "merchant": "example books",
- "amount": 4199,
- "currency": "usd"
- }'
-```
-
-
-
-the api accepts an agentcard card update only while the item is `requested` or
-`ready`. if it is `pending_approval`, finish and reconcile that authorization
-before preparing another purchase. if it is `degraded`, retrieve it to allow
-recovery and stop if it remains degraded. `update` replaces the full `spec`, so
-include `card_id` again when you want to keep the card pinned. never update an item
-to retry a failed, timed-out, or indeterminate checkout.
+for a purchase with different details, create a new card item under a new key,
+such as `book-order`, from that purchase's verified object. if the existing item
+is `pending_approval`, finish and reconcile that authorization before preparing
+another purchase. delete the old item when you no longer need it. never create
+a new item to retry a failed, timed-out, or indeterminate checkout.
-agentcard has no per-item `test`, `merchant_url`, or domain allowlist. `merchant` is the name shown on the approval screen, not an enforced browsing origin. sandbox or live behavior comes from the agentcard credential configured for the integration and must match your application-owned `AGENTCARD_MODE` assertion before you use the aliases.
+agentcard has no per-item `test` flag or domain allowlist, and its cards aren't bound to a checkout page. `merchant` is the name shown on the approval screen, not an enforced browsing origin. sandbox or live behavior comes from the agentcard credential configured for the integration and must match your application-owned `AGENTCARD_MODE` assertion before you use the aliases.
## Complete the first checkout
@@ -515,7 +444,7 @@ use this sequence for an agentcard checkout:
2. create a headful browser with the vault attached, surface `browser_live_view_url` through your trusted application, and navigate to the checkout. keep this same browser for verification and submission so location-dependent pricing cannot change between the confirmed purchase and the outgoing request.
3. independently verify the merchant, items, active presentment amount, and active presentment currency from the merchant's trusted order or cart backend. if one isn't available, use documented structured checkout data or deterministic extraction for that checkout. for a stripe payment link specifically, prefer `account_settings.display_name`, `line_item_group.total`, `line_item_group.currency`, and `line_item_group.line_items` from the structured payment-link response. use dom text and test ids only as supplemental checks because stripe can duplicate or omit them across layouts.
4. collect merchant-required fields such as email, billing name, and postal code from the end user. identify checkout-specific agent disclosures and instruct the agent to answer them truthfully in the normal form.
-5. show the verified purchase to the end user. after confirmation, create or update the card item from that same frozen object and require it to be `ready`. the vault attachment covers items created later in the same vault.
+5. show the verified purchase to the end user. after confirmation, create the card item from that same frozen object and require it to be `ready`. the vault attachment covers items created later in the same vault.
6. start the card and event observer before checkout submission. keep it running concurrently while the browser request is held.
7. give the browser agent the aliases, separately collected customer fields, and any required disclosure answer. submit the merchant form once and never retry submission.
8. publish the approval action through the authenticated, expiring application flow. never send it to the checkout browser or agent.
@@ -669,9 +598,8 @@ event id, classify the attempt as indeterminate, and don't resubmit checkout.
## Handle checkout approval
-agentcard doesn't advertise the `authorize` operation. authorization begins
-only after an attached browser submits a recognized processor request containing
-the aliases.
+authorization begins only after an attached browser submits a recognized
+processor request containing the aliases.
while the request is held, retrieve the card and send `action.url` through the
same authenticated, expiring user-action flow used for enrollment. stop serving
@@ -725,7 +653,7 @@ wallet and card items accept these `spec` fields. fields not listed here are rej
agentcard accepts amounts from 1–9007199254740991 and a three-letter `currency`. `merchant` accepts 1–120 printable characters without control characters. `card_id` uses the provider's `vc_` identifier, and wallet `user_id` uses its `usr_` identifier.
-card updates replace the complete `spec`; they are not partial merges. agentcard card items can update while `requested` or `ready`, but not while approval is pending.
+card items are immutable. create a new item to change any `spec` field.
### Item states
diff --git a/integrations/wallets/overview.mdx b/integrations/wallets/overview.mdx
index b23fd4cf..1726ad7c 100644
--- a/integrations/wallets/overview.mdx
+++ b/integrations/wallets/overview.mdx
@@ -5,7 +5,7 @@ description: "Connect user wallets through KERNEL's native integrations"
KERNEL's native wallet integrations connect a user's payment method to vault items and expose provider-hosted connection and approval through the KERNEL api. your application works with item references, advertised operations, and outcomes instead of passing raw card details to the agent.
-wallet providers supply payment material and determine approval and reuse behavior; they are not the merchant's payment processor. link supplies approved single-use cards that you inject through KERNEL's [`fill` api](/vaults/fill), keeping the values out of your application's injection flow and model context. agentcard uses aliases and provider-executed requests instead of `fill`.
+wallet providers supply payment material and determine approval and reuse behavior; they are not the merchant's payment processor. link supplies an approved one-use credential, a link pay token or a virtual card, that KERNEL injects through its [`fill` api](/vaults/fill), keeping the values out of your application's injection flow and model context. agentcard uses aliases and provider-executed requests instead of `fill`.
for the platform capabilities, benefits, and end-to-end lifecycle, start with [payments on KERNEL](/browsers/payments).
@@ -22,7 +22,7 @@ see [how payments work on KERNEL](/browsers/payments#how-payments-work) for the
icon="link"
>
collect a link wallet and approve a one-use credential for a specific
- purchase.
+ checkout.
+
+```typescript TypeScript
+const browser = await kernel.browsers.create({
+ vaults: [{ id: vault.id }],
+ headless: false,
+ timeout_seconds: 1800,
+});
+// navigate the agent to checkout, then read the exact current top-level url
+const checkoutURL = "https://shop.example.com/checkout?cart=cart_8472";
+```
+
+```python Python
+browser = kernel.browsers.create(
+ vaults=[{"id": vault.id}],
+ headless=False,
+ timeout_seconds=1800,
+)
+# navigate the agent to checkout, then read the exact current top-level url
+checkout_url = "https://shop.example.com/checkout?cart=cart_8472"
+```
+
+```bash CLI
+kernel browsers create --vault user-12345 -o json
+```
+
+
+
+`checkoutURL` / `checkout_url` is the browser's exact current top-level https url, including path, query, and fragment, obtained through your browser-control method. verify the merchant, amount, currency, and items in your controller before creating the card. see [verify and confirm the purchase](/browsers/enable-payments-in-browser-agent#2-verify-and-confirm-the-purchase).
+
+## Create a card item at checkout
+
+creating the card item starts link's spend request and the user's approval. use the session id from the browser above, not a browser name.
@@ -386,33 +429,32 @@ let card = await kernel.vaults.items.upsert("notebook-order", {
spec: {
provider: "link",
wallet: wallet.key,
+ browser_id: browser.session_id,
+ page_url: checkoutURL,
payment_method_id: paymentMethod.id,
amount: 2306,
currency: "usd",
merchant_name: "example shop",
- merchant_url: "https://shop.example.com",
context:
"buy one notebook from example shop for a total of 23.06 usd, including tax " +
"and shipping. this request is for this purchase only and must not be repeated.",
},
});
-card = await kernel.vaults.items.retrieve(card.key, { id_or_name: vault.id });
-if (!card.available_operations.some(({ type }) => type === "authorize")) {
- throw new Error("authorization is unavailable");
-}
-
-card = await kernel.vaults.items.performOperation(card.key, {
- id_or_name: vault.id,
- type: "authorize",
-});
if (card.action && "url" in card.action) {
await presentProviderAction({
userID: authenticatedUser.id,
vaultID: vault.id,
item: card,
});
+} else if (card.action?.name === "push_approval") {
+ console.log("complete the approval in your link app");
}
+
+card = await kernel.vaults.items.retrieve(card.key, {
+ id_or_name: vault.id,
+ wait: 60,
+});
```
```python Python
@@ -423,11 +465,12 @@ card = kernel.vaults.items.upsert(
spec={
"provider": "link",
"wallet": wallet.key,
+ "browser_id": browser.session_id,
+ "page_url": checkout_url,
"payment_method_id": payment_method.id,
"amount": 2306,
"currency": "usd",
"merchant_name": "example shop",
- "merchant_url": "https://shop.example.com",
"context": (
"buy one notebook from example shop for a total of 23.06 usd, including tax "
"and shipping. this request is for this purchase only and must not be repeated."
@@ -435,57 +478,115 @@ card = kernel.vaults.items.upsert(
},
)
-card = kernel.vaults.items.retrieve(card.key, id_or_name=vault.id)
-if not any(operation.type == "authorize" for operation in card.available_operations):
- raise RuntimeError("authorization is unavailable")
-
-card = kernel.vaults.items.perform_operation(
- card.key,
- id_or_name=vault.id,
- type="authorize",
-)
if card.action is not None and hasattr(card.action, "url"):
present_provider_action(
user_id=authenticated_user.id,
vault_id=vault.id,
item=card,
)
+elif card.action is not None and card.action.name == "push_approval":
+ print("complete the approval in your link app")
+
+card = kernel.vaults.items.retrieve(card.key, id_or_name=vault.id, wait=60)
```
```bash CLI
+# browser_id is the session_id returned by kernel browsers create
kernel vaults cards create user-12345 notebook-order \
--provider link \
--spec '{
"wallet": "link-wallet",
+ "browser_id": "k7q2m9x4p1w8",
+ "page_url": "https://shop.example.com/checkout?cart=cart_8472",
"payment_method_id": "pm_123",
"amount": 2306,
"currency": "usd",
"merchant_name": "example shop",
- "merchant_url": "https://shop.example.com",
"context": "buy one notebook from example shop for a total of 23.06 usd, including tax and shipping. this request is for this purchase only and must not be repeated."
}'
-kernel vaults items get user-12345 notebook-order -o json
-kernel vaults items invoke user-12345 notebook-order authorize --open
kernel vaults items get user-12345 notebook-order --wait 60 -o json
```
-`amount` uses minor currency units, so `2306` means 23.06 usd. link accepts values from 1 to 50000. `context` must contain at least 100 characters. card creation is live-only, and `spec.test` is not supported.
+the new card returns `state.status: pending_authorization` and an `action`. `spend_approval` includes a `url` the user opens to approve the purchase in link; `push_approval` means the user approves in their link app. after approval, the card becomes `ready` and advertises `fill`. one bounded wait can finish before approval does; retrieve the item again rather than creating another card.
+
+`amount` uses minor currency units, so `2306` means 23.06 usd. `context` must contain at least 100 characters. card creation is live-only, and `spec.test` is not supported.
+
+### How KERNEL pays
+
+when you create the card, KERNEL inspects the checkout page through [WebMCP](/browsers/webmcp) and chooses how link pays. you never choose or see the mode; read the card's advertised `fill` operation instead.
-`merchant_url` supplies provider context and restricts the destination for card use. see [fill the checkout](#fill-the-checkout) for exact page and origin requirements; `state.domains` is metadata, not an authorization rule.
+| checkout | payment credential | fill inputs | maximum `amount` |
+| --- | --- | --- | --- |
+| a stripe checkout page that exposes link pay token WebMCP tools | a merchant-bound link pay token that KERNEL passes to the page's WebMCP tool | `browser_id` and `page_url` only; no field selectors | 500000 |
+| any other checkout | a one-time virtual card that KERNEL writes into the page's card fields | `browser_id`, `page_url`, and `fields` selectors | 50000 |
+
+don't call the page's WebMCP tools yourself or pass a merchant account id. if the checkout doesn't support link pay tokens and `amount` exceeds 50000, creation returns `400`.
+
+### Change or retry a request
+
+card items are immutable. repeating the same `upsert` with an identical `spec` returns the existing item without inspecting the checkout again or repeating approval. a different `spec` at the same key returns `409`; `PATCH` isn't supported for cards.
+
+to change the purchase, browser, or checkout page, create a new card item under a new key. delete the old item when you no longer need it; deletion can be blocked while a provider outcome is unresolved. never create a replacement card to retry a payment whose outcome is uncertain.
+
+### Handle creation errors
+
+checkout inspection runs before KERNEL creates the item or contacts link. these errors mean no card was created and no approval started, so you can correct the cause and create the card again:
+
+| status | code | cause |
+| --- | --- | --- |
+| `400` | `ambiguous_page`, `timeout` | the page exposes conflicting WebMCP tools, or inspection didn't finish in time |
+| `403` | `destination_denied` | `page_url` isn't a valid https checkout url |
+| `404` | `browser_not_found` | the browser session doesn't exist |
+| `409` | `browser_unavailable` | the browser was deleted, belongs to another project, doesn't have the vault attached, or is busy with another vault operation |
+| `429` | — | rate limited; back off before creating the card again |
+| `500` | `browser_error` | checkout inspection failed in the browser |
## Fill the checkout
-after the user completes approval, retrieve the card with `wait: 60`. continue only when it is `ready` and advertises `fill`; one bounded wait may finish before approval does. the examples below show the fill request; for the full controller and agent handoff, follow the [browser agent payments guide](/browsers/enable-payments-in-browser-agent#4-fill-payment-fields).
+after the user completes approval, retrieve the card with `wait: 60`. continue only when it's `ready` and advertises `fill`. read that operation's `description`: it names the inputs this card needs. the examples below show the fill request; for the full controller and agent handoff, follow the [browser agent payments guide](/browsers/enable-payments-in-browser-agent#4-prepare-payment-input-and-submit-once).
+
+`browser_id` and `page_url` must exactly match the card's `spec.browser_id` and `spec.page_url`, and that page must still be open. any other browser or page returns `403 destination_denied`. the card must remain ready, unexpired, and undeleted, with an existing parent wallet. `timeout_ms` is optional.
+
+`fill` never submits payment or clicks buttons. after a `completed` fill, the agent clicks the checkout's pay button once.
-supply `browser_id`, exact current top-level `page_url`, and ordered `fields` bindings. the browser must have the vault attached in the same project. the card must remain ready, unexpired, and undeleted, with stored encrypted material and an existing parent wallet. combined `expiration` requires `format: "MM/YY"` or `"MM/YYYY"`; `timeout_ms` is optional.
+### Fill a link pay token checkout
-`page_url` is required, must use https without embedded credentials, and must share the https origin of `spec.merchant_url`. the origin comparison includes the host and port (default `443` is normalized); it does not allow other subdomains. the checkout path can differ from the stored merchant url, but the request must name the exact current page. a disallowed origin returns `403 destination_denied`. descendant payment frames can have different origins; the restriction applies to the top-level merchant page.
+when the advertised `fill` description says to omit fields, send only `browser_id` and `page_url`. KERNEL supplies the approved link pay token to the checkout through its verified WebMCP tool, and the result's `fields` array is empty.
+
+
+
+```typescript TypeScript
+const result = await kernel.vaults.items.performOperation(card.key, {
+ id_or_name: vault.id,
+ type: "fill",
+ browser_id: browser.session_id,
+ page_url: checkoutURL,
+});
+```
+
+```python Python
+result = kernel.vaults.items.perform_operation(
+ card.key,
+ id_or_name=vault.id,
+ type="fill",
+ browser_id=browser.session_id,
+ page_url=checkout_url,
+)
+```
+
+```bash CLI
+jq -n --arg browser "$BROWSER_ID" --arg url "$PAGE_URL" \
+ '{browser_id: $browser, page_url: $url}' |
+ kernel vaults items invoke user-12345 notebook-order fill --spec-file - -o json
+```
+
+
### Map card fields to inputs
-the examples below continue after wallet connection and spend approval with a ready `notebook-order` card, the same project-scoped `kernel` client with retries disabled, and a `browser` created with its vault attached. `checkoutURL` / `checkout_url` is the browser's exact current top-level url, obtained through your browser-control method; no playwright page object is required. verify the purchase and destination before calling fill; neither a model-proposed url nor a selector authorizes disclosure.
+when the advertised `fill` description asks for field/selector bindings, KERNEL writes the one-time virtual card into the page's card inputs. the examples below continue with a ready `notebook-order` card, the same project-scoped `kernel` client with retries disabled, and the `browser` and `checkoutURL` / `checkout_url` the card is bound to. verify the purchase and destination before calling fill; neither a model-proposed url nor a selector authorizes disclosure.
| card field | value and format |
| --- | --- |
@@ -559,7 +660,7 @@ if result.status != "completed":
```
```bash CLI
-# BROWSER_ID is the attached session ID; PAGE_URL is its exact current checkout URL.
+# BROWSER_ID and PAGE_URL are the card's spec.browser_id and spec.page_url.
# Inspect the item and require the advertised fill operation before invoking it.
kernel vaults items get user-12345 notebook-order -o json
jq -n --arg browser "$BROWSER_ID" --arg url "$PAGE_URL" '{
@@ -576,17 +677,21 @@ jq -n --arg browser "$BROWSER_ID" --arg url "$PAGE_URL" '{
+if a completed fill reveals another card field, inspect the page and send a separate fill containing only the newly visible field. don't resubmit fields that were already filled.
+
### Handle the outcome
the cli exits nonzero for `failed` or `unknown`, but retains the value-free result on stdout with `-o json`. preserve that output and its per-field statuses; don't discard it or retry just because the exit code is nonzero. a transport error can leave the outcome uncertain even without a result body.
see the [shared fill outcome contract](/vaults/fill#handle-the-outcome) for per-field statuses and reconciliation.
-`fill` returns value-free per-field outcomes. `completed` means the fields were filled, not that payment succeeded. a `failed` result can leave earlier writes in place; `unknown` or a lost response requires reconciliation. don't automatically retry fill or fall back to aliases. decide whether to submit separately, and never retry checkout automatically.
+`fill` returns a value-free outcome. `completed` means KERNEL supplied the payment credential to the page, not that payment succeeded. a `failed` result can leave earlier writes in place; `unknown` or a lost response requires reconciliation. don't automatically retry fill or fall back to aliases. decide whether to submit separately, and never retry checkout automatically.
-single-use describes the provider-issued card, not one-use alias consumption. `fill` does not consume the item or clear its encrypted material on the first field write. expiry, deletion, and item lifecycle restrictions still apply; a ready item is not evidence that a purchase can safely be repeated.
+validation errors before any write return `400`, `403`, `404`, or `409` with an error code such as `destination_denied`, `target_changed`, `ambiguous_page`, `browser_unavailable`, `field_unavailable`, `timeout`, or `execution_failed`. inspect and correct the cause before deciding on a new fill.
-don't repeat `authorize` or create a replacement item to retry an unknown purchase. inspect outcomes, item events, and the merchant's order state first.
+one-time describes the provider-issued credential, not item consumption. `fill` doesn't consume the item or clear its encrypted material on the first write. expiry, deletion, and item lifecycle restrictions still apply; a ready item isn't evidence that a purchase can safely be repeated.
+
+don't create a replacement item to retry an unknown purchase. inspect outcomes, item events, and the merchant's order state first.
## Item reference
@@ -595,23 +700,25 @@ wallet and card items accept these `spec` fields. fields not listed here are rej
| item | required fields | optional fields |
| --- | --- | --- |
| wallet | `provider: 'link'`, `authorization.method: 'oauth'`, `authorization.client` | write-only `authorization.tokens` is required only with a customer-managed client |
-| card | `provider`, `wallet`, `payment_method_id`, `amount`, `currency`, `merchant_name`, `merchant_url`, `context` | `line_items`, `totals`, `metadata`, `expires_at` |
+| card | `provider`, `wallet`, `browser_id`, `page_url`, `payment_method_id`, `amount`, `currency`, `merchant_name`, `context` | `line_items`, `totals`, `metadata`, `expires_at` |
+
+wallet items also return a read-only `description` with server-generated guidance for agents.
the default link client is `{type: 'kernel_managed'}`. for your own client, set
`authorization.client` to `{type: 'customer_managed', provider_config: {name: 'checkout-link'}}`
and supply `authorization.tokens` with `access_token` and `refresh_token`.
-`amount` uses minor currency units. link accepts 1–50000, requires a three-letter `currency`, limits `merchant_name` to 255 characters, requires an absolute http or https `merchant_url`, and requires at least 100 characters in `context`. its optional `expires_at` is a unix timestamp in seconds.
+`browser_id` is a running browser session id with the vault attached, not a reusable browser name. `page_url` is the exact final checkout url and must use https without embedded credentials. `amount` uses minor currency units: 1–500000 when the checkout supports link pay tokens and 1–50000 otherwise. link requires a three-letter `currency`, limits `merchant_name` to 255 characters, and requires at least 100 characters in `context`. its optional `expires_at` is a unix timestamp in seconds.
link `line_items` support `name`, `quantity`, `unit_amount`, `description`, `sku`, `url`, `image_url`, `product_url`, and `totals`. each `totals` entry supports `type`, `display_text`, and `amount`. link `metadata` accepts string values.
-card updates replace the complete `spec`; they are not partial merges. link card items can update only while `requested`.
+card items are immutable. create a new item to change any `spec` field.
### Item states
- **wallet:** `pending_authorization`, `connected`, `declined`, `reconnect_required`, `degraded`
- **card:** `requested`, `pending_authorization`, `ready`, `consumed`, `expired`, `declined`, `recovery_required`
-card state can include `masks.brand` and `masks.last4`. retrieve the card's advertised operations before using it. for an unresolved provider outcome, follow [payment recovery](/integrations/wallets/overview#payment-actions-and-recovery); a ready item alone is not evidence that a purchase can safely be repeated.
+card state can include `masks.brand` and `masks.last4`. retrieve the card's advertised operations before using it. for an unresolved provider outcome, follow [payment recovery](/integrations/wallets/overview#payment-actions-and-recovery); a ready item alone isn't evidence that a purchase can safely be repeated.
deleting a card clears its stored provider value. deleting a wallet also invalidates its dependent cards.
diff --git a/vaults/fill.mdx b/vaults/fill.mdx
index 8d947fc3..78923df3 100644
--- a/vaults/fill.mdx
+++ b/vaults/fill.mdx
@@ -19,6 +19,8 @@ retrieve the item and require `fill` in `available_operations`. KERNEL rechecks
the item must be ready and contain a stored value for each requested field. additional eligibility rules depend on the item and its provider; don't assume every item supports `fill`.
+read the advertised operation's `description` before invoking it. it names the inputs that item needs. credentials and link virtual cards need `fields` bindings; a link card paid with a link pay token needs only `browser_id` and `page_url`, and its result has an empty `fields` array.
+
attach the vault when creating the browser. the browser and vault must belong to the same project, and the attachment can't change later. attaching a vault grants access to all its items, including items added later; use separate vaults for tasks that must not share items. use the browser's session id in `browser_id`, not its name. KERNEL rechecks the attachment and item lifecycle before writing.
configure your sdk client with `maxRetries: 0` (typescript) or `max_retries=0` (python) for fill, and don't wrap it in a retry loop. the cli does not automatically retry fill requests.
@@ -27,7 +29,7 @@ configure your sdk client with `maxRetries: 0` (typescript) or `max_retries=0` (
## Map fields to inputs
-your request names the item and vault, the attached browser's `browser_id`, the current `page_url`, and field-selector bindings. field names come from the item's schema; they are not raw values. the following example uses a credential item with username and password fields.
+your request names the item and vault, the attached browser's `browser_id`, the current `page_url`, and, when the item needs them, field-selector bindings. field names come from the item's schema; they are not raw values. the following example uses a credential item with username and password fields.
`fill` reads values from a ready credential item. if another vault
@@ -95,6 +97,8 @@ if result.status != "completed":
when supplied, `page_url` must exactly match one current top-level page, including path, query, and fragment. it isn't a navigation instruction or a prefix match. zero or multiple matching pages fail.
+**link cards:** `browser_id` and `page_url` must exactly match the card's `spec.browser_id` and `spec.page_url`; any other destination returns `403 destination_denied`.
+
**credentials:** omit `page_url` only when the browser has exactly one open page. credential items have no destination allowlist. your trusted controller must authorize the destination before disclosing credentials; neither `description` nor `page_url` grants or restricts that permission.
each selector must identify exactly one editable input or select across the main frame and all descendant frames. a selector may identify a container only if it resolves to one unique editable element inside it. zero matches, multiple matches, or two bindings targeting the same element fail validation. selects match option values, not labels.
@@ -112,7 +116,7 @@ the cli exits nonzero for `failed` or `unknown`, but retains the value-free resu
| `unknown` | stop and reconcile; at least one field's outcome can't be determined |
| transport error | treat the outcome as uncertain because writes may already have happened |
-known execution failures can return http `200` with a `failed` or `unknown` status. inspect the response body, not only the http status. each entry in `fields` identifies its request binding by zero-based `index` and reports `filled`, `failed`, `unknown`, or `not_attempted`. bindings after the first failed or unknown field are `not_attempted`.
+known execution failures can return http `200` with a `failed` or `unknown` status. inspect the response body, not only the http status. each entry in `fields` identifies its request binding by zero-based `index` and reports `filled`, `failed`, `unknown`, or `not_attempted`. bindings after the first failed or unknown field are `not_attempted`. `fields` is empty when the request had no bindings.
`fill` doesn't click buttons or submit forms, but input/change handlers can trigger site behavior. `completed` doesn't mean login succeeded, a form was submitted, or payment succeeded.
@@ -122,4 +126,4 @@ known execution failures can return http `200` with a `failed` or `unknown` stat
- [use vault credentials in a browser agent](/browsers/use-vault-credentials-in-browser-agent) for the full application and agent handoff.
- [payments on KERNEL](/browsers/payments) for using vault items in checkout.
-- [link card requirements](/integrations/wallets/stripe-link#fill-the-checkout) for card fields and merchant restrictions. [agentcard](/integrations/wallets/agentcard) uses an alias-based flow instead of `fill`.
+- [link card requirements](/integrations/wallets/stripe-link#fill-the-checkout) for link pay token and virtual-card fills and their checkout binding. [agentcard](/integrations/wallets/agentcard) uses an alias-based flow instead of `fill`.
diff --git a/vaults/overview.mdx b/vaults/overview.mdx
index 2e272415..d39caf1e 100644
--- a/vaults/overview.mdx
+++ b/vaults/overview.mdx
@@ -66,7 +66,7 @@ credential backing isn't currently available.
### Inject values with KERNEL's fill api
-retrieve the item and require `fill` in `available_operations`. your controller authorizes the destination and supplies field names and selectors, not the stored values. KERNEL checks the browser attachment and item lifecycle, validates the target inputs, and writes values into the attached browser.
+retrieve the item and require `fill` in `available_operations`. your controller authorizes the destination and supplies the inputs the operation's description names, such as field names and selectors, not the stored values. KERNEL checks the browser attachment and item lifecycle, validates the target inputs, and writes values into the attached browser.
`fill` returns value-free per-field outcomes. it doesn't submit a form or establish the site's acceptance. inspect failed or unknown outcomes before taking another action; don't automatically retry. see [fill browser fields](/vaults/fill) for the request and outcome contract.
@@ -86,7 +86,7 @@ is required for browser operations, including `fill`.
| item | typed resource addressed by an immutable `key`: `credential`, `wallet`, or `card` |
| alias | non-sensitive, format-valid stand-in returned in state for eligible items; aliases belong to that item |
| action | user interaction returned as `action`, such as a hosted collection or approval url |
-| operation | api action advertised in `available_operations`: `collect`, `authorize`, `prepare_checkout`, or `fill`, when eligible |
+| operation | api action advertised in `available_operations`: `collect`, `prepare_checkout`, or `fill`, when eligible |
| expansion | live provider data advertised in `available_expansions`; the initial expansion is `payment_methods` |
| event | immutable item observation with `id`, `name`, optional `browser_id`, `data`, and `created_at` |
| browser attachment | vault reference fixed when the browser is created |
From a9b8a1062835f9936c01e5636b06eb9558abbcdc Mon Sep 17 00:00:00 2001
From: rgarcia <72655+rgarcia@users.noreply.github.com>
Date: Fri, 25 Sep 2026 02:05:43 +0000
Subject: [PATCH 2/2] Keep Link page intro high-level
---
integrations/wallets/stripe-link.mdx | 11 +++--------
1 file changed, 3 insertions(+), 8 deletions(-)
diff --git a/integrations/wallets/stripe-link.mdx b/integrations/wallets/stripe-link.mdx
index 07717a88..98f46bb7 100644
--- a/integrations/wallets/stripe-link.mdx
+++ b/integrations/wallets/stripe-link.mdx
@@ -5,14 +5,9 @@ description: "use link by stripe to approve a one-use payment credential for a b
[link by stripe](https://stripe.com/payments/link) connects a user's wallet through oauth and issues a one-use payment credential for an approved purchase. KERNEL's native integration handles wallet connection, token refresh, approval actions, and credential storage through vault items. oauth credentials and payment material use kms-backed envelope encryption.
-you create a link card item once the agent reaches the final checkout page in a browser with the vault attached. KERNEL inspects that page and picks how link pays:
+you create a link card item at the final checkout page, and KERNEL decides how link pays for that checkout. KERNEL's [`fill` api](/vaults/fill) then puts the approved credential into the attached browser without passing it through your application's code or model context. see [how KERNEL pays](#how-kernel-pays) for details.
-- **link pay token:** when the checkout is a stripe checkout page that exposes link pay token [WebMCP](/browsers/webmcp) tools, KERNEL uses a merchant-bound link pay token. `fill` needs no field selectors.
-- **virtual card:** otherwise, link issues a one-time virtual card that KERNEL's [`fill` api](/vaults/fill) writes into the page's card fields.
-
-you never choose the mode. either way, `fill` puts the approved credential into the attached browser without passing it through your application's code or model context, and returns outcomes, not values. `fill` never submits payment; the agent clicks pay afterward.
-
-link is the wallet provider, not the merchant's payment processor. the virtual-card path doesn't require a native processor adapter or a stripe merchant, but it needs uniquely selectable card inputs on the checkout page. for the overall offering, see [payments on KERNEL](/browsers/payments).
+link is the wallet provider, not the merchant's payment processor. for the overall offering, see [payments on KERNEL](/browsers/payments).
## Before you start
@@ -522,7 +517,7 @@ when you create the card, KERNEL inspects the checkout page through [WebMCP](/br
| a stripe checkout page that exposes link pay token WebMCP tools | a merchant-bound link pay token that KERNEL passes to the page's WebMCP tool | `browser_id` and `page_url` only; no field selectors | 500000 |
| any other checkout | a one-time virtual card that KERNEL writes into the page's card fields | `browser_id`, `page_url`, and `fields` selectors | 50000 |
-don't call the page's WebMCP tools yourself or pass a merchant account id. if the checkout doesn't support link pay tokens and `amount` exceeds 50000, creation returns `400`.
+the virtual-card path doesn't require a native processor adapter or a stripe merchant, but it needs uniquely selectable card inputs on the checkout page. don't call the page's WebMCP tools yourself or pass a merchant account id. if the checkout doesn't support link pay tokens and `amount` exceeds 50000, creation returns `400`.
### Change or retry a request