> ## Documentation Index
> Fetch the complete documentation index at: https://www.kernel.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Enable Payments in a Browser Agent

> connect a wallet, approve a purchase, and complete checkout with your browser agent

add payments to your existing browser agent without passing card numbers or cvc
through your application's code or model context. connect a wallet once, then
verify and approve each purchase before completing checkout.

## Before you start

* have an existing browser agent. this guide adds payment handling, not navigation or reasoning.
* choose a wallet integration from the [wallet overview](https://www.kernel.sh/docs/integrations/wallets/overview.md), 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. on stripe checkouts that support link pay tokens, KERNEL fills the payment without field selectors; elsewhere you need verified field selectors for the virtual card. agentcard requires a [native processor adapter](https://www.kernel.sh/docs/integrations/wallets/overview.md#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.

use KERNEL-managed credentials by default. optional client setup belongs in your
controller: see [link](https://www.kernel.sh/docs/integrations/wallets/stripe-link.md#bring-your-own-link-oauth-client)
or [agentcard](https://www.kernel.sh/docs/integrations/wallets/agentcard.md#bring-your-own-agentcard-oauth-client).

### Roles and resources

* **controller:** your trusted application code. it calls KERNEL, verifies purchases, authorizes field bindings, presents user actions, and observes payment state.
* **agent:** your browser automation. it proposes purchase details and selectors, then completes the approved checkout.
* **user:** the person who connects a payment method, confirms the purchase, and completes provider approval.

a **vault** groups KERNEL items. a **wallet item** represents a provider connection;
a **card item** references that wallet in the same vault and tracks payment input
and authorization state. neither item is the user's underlying wallet or card.

### Shared safety rules

> [!WARNING]
>
> * 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.
> * submit checkout once through the merchant's normal form. don't retry submission or fall back from `fill` to aliases after failure or uncertainty. `fill` never submits, so a retry is safe. retry only after checking the page, and never in an automatic loop.
> * 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.

## One-time setup

### 1. Create a vault

scope the client and vault to the project that will own the browser session.
these clients disable automatic sdk retries.

```typescript TypeScript theme={null}
import Kernel from "@onkernel/sdk";

const projectID = process.env.KERNEL_PROJECT_ID;
if (!projectID) throw new Error("set KERNEL_PROJECT_ID");

const kernel = new Kernel({ projectID, maxRetries: 0 });
const vault = await kernel.vaults.upsert({ name: "user-12345" });
```

```python Python theme={null}
import os

from kernel import Kernel

kernel = Kernel(
    project_id=os.environ["KERNEL_PROJECT_ID"],
    max_retries=0,
)
vault = kernel.vaults.upsert(name="user-12345")
```

```bash CLI theme={null}
kernel vaults create --name user-12345
```

`vaults.upsert` creates the vault or retrieves one with the same name. vault names
are immutable within the project.

### 2. Connect a wallet

allow at most one wallet item per provider in a vault. list the vault's items and
group wallets by `spec.provider` before rendering your payment settings:

| existing wallet for the provider | ui behavior |
| - | - |
| none | show the option to connect that provider |
| `pending_authorization` | hide the add option and resume the existing hosted action |
| `connected` | hide the add option, show the provider as connected, and reuse the existing wallet |
| any other state | hide the add option and show the existing state; recover it or use an explicit remove-and-replace flow |

recheck in your controller immediately before creating a wallet. the api makes
item keys unique, not providers, so a different key can create a duplicate wallet.
deleting a wallet invalidates its dependent card items; use an explicit
remove-and-replace flow instead of adding a second wallet.

1. let the user select a provider that doesn't already have a wallet in the vault.
2. create the wallet item through your controller, following the [link setup](https://www.kernel.sh/docs/integrations/wallets/stripe-link.md) or [agentcard setup](https://www.kernel.sh/docs/integrations/wallets/agentcard.md).
3. present the returned action using the authenticated flow below. the user connects or enrolls their real payment method with the provider.
4. wait for the wallet's status to become `connected` before preparing a purchase.

### Present hosted actions in your application

provider action urls are bearer-like handoffs to enrollment or approval. route
them through your controller:

1. your controller retrieves the item and keeps the raw action url out of logs, analytics, and model context.
2. store the action server-side under an opaque id bound to the authenticated user, vault id, item key, and action name.
3. render a link to your own authenticated action endpoint. before redirecting, verify the session owns that binding and the item still returns the same action.
4. send the redirect with `Cache-Control: no-store` and `Referrer-Policy: no-referrer`.
5. apply a short application ttl capped by `item.expires_at` or `state.authorization.expires_at` when present. invalidate the record immediately when the action changes, disappears, or reaches a terminal state.

the `presentProviderAction` functions later in this guide represent this
application-owned flow. the checkout agent and its browser must never receive
the raw provider url.

## For each purchase

### 1. Attach the vault to a browser

vault attachments are fixed at browser creation. use the same project-scoped client that created the vault.

```typescript TypeScript theme={null}
const browser = await kernel.browsers.create({
  vaults: [{ id: vault.id }],
  headless: false,
  timeout_seconds: 1800,
});

if (!browser.browser_live_view_url) {
  throw new Error("headful browser did not return a live view url");
}
await presentLiveView({
  userID: authenticatedUser.id,
  sessionID: browser.session_id,
  url: browser.browser_live_view_url,
});
```

```python Python theme={null}
browser = kernel.browsers.create(
    vaults=[{"id": vault.id}],
    headless=False,
    timeout_seconds=1800,
)

if browser.browser_live_view_url is None:
    raise RuntimeError("headful browser did not return a live view url")
present_live_view(
    user_id=authenticated_user.id,
    session_id=browser.session_id,
    url=browser.browser_live_view_url,
)
```

```bash CLI theme={null}
kernel browsers create --vault user-12345 -o json
```

`browser_live_view_url` lets the user watch the checkout during confirmation
and agentcard approval pauses. `presentLiveView` represents your authenticated
user-facing page: keep the url server-side with the user and browser session
binding, render or embed it only after checking that session, and remove it when
you delete or time out the browser. don't log the url or put it in model context.
see [live view](https://www.kernel.sh/docs/browsers/live-view.md#embedding-in-an-iframe) for iframe and csp
requirements.

connect your existing agent to `browser.cdp_ws_url`. see [Controlling a Browser](https://www.kernel.sh/docs/introduction/control.md) for supported connection options.

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.

### 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.
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.

for stripe payment links, see [checkout-specific notes](#checkout-specific-notes)
for structured response fields and adaptive pricing.

### 3. Collect other checkout fields

collect required email, billing, shipping, phone, and other customer fields from
the user or their previously approved backend data. pass them separately from
payment input; don't ask the agent to invent missing values.

answer any agent disclosure truthfully in the merchant's form and verify the
control is selected before submission. if a required field or disclosure can't
be completed and verified, stop. see [checkout-specific notes](#checkout-specific-notes)
for stripe's disclosure controls.

### 4. Prepare payment input and submit once

follow the tab for your provider. each path uses the frozen purchase object from
step 2, a connected wallet in the same vault, and this browser session.

> [tab] Link by Stripe
>
> #### Prepare and authorize the card
>
> 1. list the wallet's payment methods and let the user select one.
> 2. with the browser on the final checkout page, [create a link card item](https://www.kernel.sh/docs/integrations/wallets/stripe-link.md#create-and-authorize-a-card-item) named `notebook-order` from the verified purchase and selected method.
> 3. require `authorize` in `available_operations` and invoke it with the browser's `browser_id` and the checkout's exact current `page_url`. KERNEL inspects the checkout and chooses a [link pay token or a virtual card](https://www.kernel.sh/docs/integrations/wallets/stripe-link.md#link-pay-tokens-and-virtual-cards). present any returned action through your controller.
> 4. wait for the card to become `ready`, then require `fill` immediately before use and follow its description. link cards never expose `state.aliases`.
>
> ```typescript TypeScript theme={null}
> const card = await kernel.vaults.items.retrieve("notebook-order", {
>   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 theme={null}
> card = kernel.vaults.items.retrieve(
>     "notebook-order",
>     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}")
> ```
>
> ```bash CLI theme={null}
> kernel vaults items get user-12345 notebook-order --wait 60 -o json
> ```
>
> #### Fill the approved fields
>
> 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 be on the origin of the checkout page you
> authorized at. no playwright page object is required. see
> [field requirements](https://www.kernel.sh/docs/integrations/wallets/stripe-link.md#fill-the-checkout).
>
> if the card's `fill` description says to omit fields, KERNEL delivered a link pay
> token: send only `browser_id` and `page_url`, as in
> [fill a link pay token](https://www.kernel.sh/docs/integrations/wallets/stripe-link.md#fill-a-link-pay-token).
> the example below is for a virtual card.
>
> `fill` supports stored [billing fields](https://www.kernel.sh/docs/integrations/wallets/stripe-link.md#map-card-fields-to-inputs)
> as well as card fields. requesting an absent value returns `field_unavailable`
> before any writes.
>
> ```typescript TypeScript theme={null}
> 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")
> ) {
>   throw new Error("fill is unavailable for this card");
> }
> 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" },
>   ],
>   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("fill did not complete; inspect per-field outcomes and retry");
> }
> ```
>
> ```python Python theme={null}
> 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)
> ):
>     raise RuntimeError("fill is unavailable for this card")
> 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"},
>     ],
>     timeout_ms=10000,
> )
> if result.type != "fill":
>     raise RuntimeError("unexpected operation response")
> print(result.status, result.fields)
> if result.status != "completed":
>     raise RuntimeError("fill did not complete; inspect per-field outcomes and retry")
> ```
>
> 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. `fill` never submits, so a retry is safe. retry only
> after checking the page, and never in an automatic loop. don't switch to
> aliases. see the
> [`fill` outcome contract](https://www.kernel.sh/docs/vaults/fill#handle-the-outcome) for details and [link checkout](https://www.kernel.sh/docs/integrations/wallets/stripe-link.md#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.

> [tab] Agentcard
>
> #### Prepare the card and aliases
>
> 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](https://www.kernel.sh/docs/integrations/wallets/agentcard.md) 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.
>
> retrieve the item and require `ready` before reading aliases:
>
> ```typescript TypeScript theme={null}
> const card = await kernel.vaults.items.retrieve("notebook-order", {
>   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 theme={null}
> card = kernel.vaults.items.retrieve(
>     "notebook-order",
>     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}")
> ```
>
> ```bash CLI theme={null}
> kernel vaults items get user-12345 notebook-order --wait 60 -o json
> ```
>
> ```typescript TypeScript theme={null}
> const agentcard = await kernel.vaults.items.retrieve(card.key, {
>   id_or_name: vault.id,
> });
> if (
>   agentcard.type !== "card" ||
>   agentcard.state.provider !== "agentcard" ||
>   agentcard.state.status !== "ready" ||
>   !agentcard.state.aliases
> ) {
>   throw new Error("agentcard aliases are unavailable");
> }
> const aliases = agentcard.state.aliases;
> ```
>
> ```python Python theme={null}
> agentcard = kernel.vaults.items.retrieve(card.key, id_or_name=vault.id)
> if (
>     agentcard.type != "card"
>     or agentcard.state.provider != "agentcard"
>     or agentcard.state.status != "ready"
>     or agentcard.state.aliases is None
> ):
>     raise RuntimeError("agentcard aliases are unavailable")
> aliases = agentcard.state.aliases
> ```
>
> aliases cover `number`, `cvc`, `exp_month`, and `exp_year`. recheck them immediately
> before use; don't cache them across expiry, state changes, or deletion.
>
> #### Observe approval before submitting
>
> start this observer before the agent submits checkout. KERNEL holds the recognized
> request while the user approves it, then replays the provider's response.
> `presentProviderAction` implements the [authenticated hosted-action flow](#present-hosted-actions-in-your-application)
> above; it must never send the action url to the agent.
>
> ```typescript TypeScript theme={null}
> let after: string | undefined;
>
> async function observePayment(stop: AbortSignal): Promise<void> {
>   while (!stop.aborted) {
>     const current = await kernel.vaults.items.retrieve(card.key, {
>       id_or_name: vault.id,
>       wait: 5,
>     });
>
>     if (current.action && "url" in current.action) {
>       await presentProviderAction({
>         userID: authenticatedUser.id,
>         vaultID: vault.id,
>         item: current,
>       });
>     } else if (current.action?.name === "push_approval") {
>       console.log("complete the approval in your wallet");
>     }
>
>     const events = await kernel.vaults.items.events(card.key, {
>       id_or_name: vault.id,
>       after,
>       wait: 5,
>     });
>     for (const event of events) {
>       console.log(event.id, event.name, event.browser_id, event.data);
>       after = event.id;
>     }
>   }
> }
> ```
>
> ```python Python theme={null}
> from threading import Event
>
>
> def observe_payment(stop: Event) -> None:
>     after = None
>
>     while not stop.is_set():
>         current = kernel.vaults.items.retrieve(
>             card.key,
>             id_or_name=vault.id,
>             wait=5,
>         )
>
>         if current.action is not None and hasattr(current.action, "url"):
>             present_provider_action(
>                 user_id=authenticated_user.id,
>                 vault_id=vault.id,
>                 item=current,
>             )
>         elif current.action is not None and current.action.name == "push_approval":
>             print("complete the approval in your wallet")
>
>         if after is None:
>             events = kernel.vaults.items.events(card.key, id_or_name=vault.id, wait=5)
>         else:
>             events = kernel.vaults.items.events(
>                 card.key,
>                 id_or_name=vault.id,
>                 after=after,
>                 wait=5,
>             )
>
>         for event in events:
>             print(event.id, event.name, event.browser_id, event.data)
>             after = event.id
>
> ```
>
> ```bash CLI theme={null}
> kernel vaults items get user-12345 notebook-order --wait 5 --open
> kernel vaults items events user-12345 notebook-order --wait 60 -o json
> ```
>
> each cli `--wait` performs one bounded observation; rerun it to observe the existing
> attempt, not to submit or authorize again. run `--open` only in your controller
> or a human-operated terminal, and keep its output outside the agent.
>
> `events` returns an ordered array. an empty array means no new observation, not
> failure. correlate `event.browser_id` when a vault is attached to multiple sessions.
>
> #### Run the checkout
>
> pass aliases and separately approved customer data as structured task input.
> replace the angle-bracketed fields in this prompt in your controller:
>
> ```text theme={null}
> Complete the checkout at https://shop.example.com for one notebook.
> The approved total is 23.06 USD.
>
> Use this payment input in the checkout form:
> - card number: <aliases.number>
> - cvc: <aliases.cvc>
> - expiry month: <aliases.exp_month>
> - expiry year: <aliases.exp_year>
>
> Use these separately collected customer fields where the checkout requires them:
> - email: <checkout.email>
> - billing name: <checkout.billing_name>
> - postal code: <checkout.postal_code>
>
> If the checkout asks whether an agent is acting for another person, select the
> truthful disclosure option in the page before submission. For example:
> - I am an AI agent acting on behalf of someone else
>
> Submit the checkout once and never retry submission. If the payment request
> pauses for user approval, wait for the controller to finish that approval. Do
> not retry payment.
> ```
>
> the agent can fill top-level fields and payment iframes. let the merchant page's
> submission code create the outgoing request; don't use a raw processor api call.
>
> the following application-owned functions represent your existing agent and order
> backend. start the observer before checkout and keep it running until the merchant
> reaches a terminal order state or your reconciliation deadline:
>
> ```typescript TypeScript theme={null}
> const stop = new AbortController();
> const observer = observePayment(stop.signal);
>
> try {
>   await runBrowserAgentCheckout({ browser, aliases, verifiedPurchase });
>   await waitForMerchantResolution(verifiedPurchase.orderID);
> } finally {
>   stop.abort();
>   try {
>     await observer;
>   } finally {
>     await kernel.browsers.deleteByID(browser.session_id);
>   }
> }
> ```
>
> ```python Python theme={null}
> from threading import Event, Thread
>
> stop = Event()
> observer = Thread(target=observe_payment, args=(stop,))
> observer.start()
>
> try:
>     run_browser_agent_checkout(
>         browser=browser,
>         aliases=aliases,
>         verified_purchase=verified_purchase,
>     )
>     wait_for_merchant_resolution(verified_purchase.order_id)
> finally:
>     stop.set()
>     try:
>         observer.join()
>     finally:
>         kernel.browsers.delete_by_id(browser.session_id)
> ```
>
> if the deadline passes without a terminal merchant state, classify the attempt as
> indeterminate, stop the observer, and retain the attempt identifiers. don't submit
> again. the observer only presents actions and reads events; it doesn't submit or
> repeat authorization. [verify the outcome](#verify-the-outcome) before reporting success.

## Verify the outcome

use the merchant's trusted order record to confirm a paid order whose merchant,
amount, currency, and items match the frozen purchase. item state, events, and the
checkout page provide supporting evidence, not proof by themselves. if the record
is unavailable or the sources disagree, keep the result indeterminate and don't retry.

| observation | next action |
| - | - |
| `payment_succeeded` | verify that the merchant created the expected order |
| `payment_requires_action` | continue the existing merchant flow without resubmitting payment |
| `payment_processing` | wait for the existing payment and inspect the merchant's status |
| `fill` `completed` or `credential_fill_attempted` | inspect per-field outcomes and merchant state; filling alone does not prove submission or payment success |
| `fill` `failed`, `unknown`, or a lost response | `fill` never submits, so a retry is safe. retry only after checking the page, and never in an automatic loop. don't fall back to aliases |
| agentcard `ready` or authorization `approved` | inspect charge, replay, and merchant state; reusable item state does not prove purchase success |
| decline, expiry, rejection, failure, abandonment, or `payment_unknown` | stop and reconcile the existing attempt before deciding whether a new purchase is appropriate |
| `recovery_required` | stop; reconcile the unresolved provider outcome. do not retry or delete the item or its parent wallet/vault |

retain the vault id, card key, browser id, and last event id until reconciliation
is complete. delete the browser after outcome inspection or your reconciliation
deadline; this doesn't undo provider execution or cancel an order. keep or delete
the vault and provider items based on future use, except when `recovery_required`
prohibits deletion.

## Checkout-specific notes

> [accordion] Stripe payment links and adaptive pricing
>
> for a stripe payment link without an order or cart backend, use the payment-link
> response as the deterministic source. the current response exposes
> `account_settings.display_name`, `line_item_group.total`,
> `line_item_group.currency`, and `line_item_group.line_items`. use dom text and
> `data-testid` attributes only as supplemental checks. stripe can render multiple
> responsive copies of a summary or omit product-level test ids in another layout,
> so don't require a specific test id or number of matching elements. these are
> stripe page details rather than a KERNEL contract. if the structured response is
> missing or its values disagree with the rendered checkout, fail verification
> instead of falling back to model inference.
>
> stripe adaptive pricing can change the checkout's displayed amount and currency
> for the browser's location. create the card item from the active presentment
> amount and currency shown to the user and submitted by that checkout, not the
> payment link's base integration amount and currency. include those active values
> in the verified purchase object and the confirmation screen.

> [accordion] Stripe agent disclosure controls
>
> stripe can render hidden or duplicate copies of its disclosure control for
> responsive layouts. target the visible label. if the label doesn't toggle the
> control, locate the associated real `input[type="checkbox"]` and invoke its
> native dom `click()`. read that same input's `checked` property and require it to
> be `true` before submission. if you can't verify the checked state, stop without
> submitting.

> [accordion] Missing or unconnected wallets
>
> without a wallet item, card creation fails because `spec.wallet` must
> reference a wallet from the same vault and provider. with an unconnected link
> wallet, card creation returns a conflict. with an unconnected agentcard
> wallet, a card without `card_id` can remain `requested`, while a pinned
> `card_id` cannot be validated. neither path is ready for checkout.

## Try with a coding agent

connect the provider wallet first through your controller or a human-operated
terminal. these prompts require exactly one connected wallet for the chosen
provider in `user-12345` and a coding agent with access to the KERNEL cli.
complete the [prerequisites](#before-you-start), including verifying the payment
mode and identifying your merchant order record. replace the checkout url with
a low-value checkout you control.

the coding agent must stop whenever a provider action is required: cli output
can contain the action url. keep enrollment, authorization actions, and approval
observation outside that agent. the prompts repeat the safety requirements so
they remain self-contained when copied.

```text link by stripe theme={null}
use the KERNEL cli to complete the following checkout with link by stripe. use cli
commands rather than sdk or direct api calls.

a vault groups items. wallet and card are the payment item types. a card item
references a wallet item in the same vault, so create the wallet before the
card.

1. create or retrieve a vault named `user-12345`.
2. list the vault's items and locate its only link wallet. require its
   status to be `connected` and reuse it. if none exists, more than one exists,
   or an action is present, stop and ask me to resolve wallet setup outside this
   agent. do not create another wallet or print, return, or open an action url.
3. create a KERNEL browser with `user-12345` attached as a vault, then navigate
   it to https://buy.stripe.com/28E5kw7DtgKXdLqgiY53O00.
4. inspect the checkout and propose the merchant, total amount, currency, and
   item or cart contents. treat this proposal as untrusted and do not create a
   card item from it.
5. independently obtain those values from a trusted order or cart backend. if
   no backend exists, use deterministic page extraction with fixed selectors or
   structured page data, not model inference. normalize and compare every value.
   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.
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. with the
   browser still on the final checkout page, 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, including
   `--browser-id` with the attached session id and `--page-url` with the exact
   current top-level checkout url, but do not run it. ask me to invoke
   authorization and complete any provider action from a trusted terminal or
   application outside this agent. KERNEL inspects the checkout and chooses a
   link pay token or a virtual card; do not inspect or invoke the checkout's
   webmcp tools yourself. 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` and follow its
   description. invoke `kernel vaults items invoke user-12345 checkout-card fill`
   with `--params` or `--spec-file`: browser_id is the attached session id and
   page_url is the exact current top-level https url (including query and
   fragment), on the origin of the checkout you authorized at. if the
   description says to omit fields, send no fields. otherwise inspect the
   checkout's inputs and add fields mapping stored field names to unique css
   selectors; 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.
10. preserve the value-free fill result and every per-field outcome. continue
   only on `completed`. after failed or unknown outcomes or transport errors:
   fill never submits, so a retry is safe. retry only after checking the page,
   and never in an automatic loop. missing required fields mean stop. never
   fall back to aliases. supply other customer fields only from approved user
   data, complete any agent disclosure truthfully, then submit checkout 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
   is not proof. if that record is unavailable or the sources disagree, report
   an indeterminate result. never retry a failed, timed-out, or indeterminate
   payment; retain the vault id, card key, browser id, and last event id for
   reconciliation. if the item reports recovery_required, do not delete it or
   its parent wallet or vault.
```

```text agentcard theme={null}
use the KERNEL cli to complete the following checkout with agentcard. use cli
commands rather than sdk or direct api calls.

a vault groups items. wallet and card are the payment item types. a card item
references a wallet item in the same vault, so create the wallet before the
card.

1. create or retrieve a vault named `user-12345`.
2. list the vault's items and locate its only agentcard wallet. require its
   status to be `connected` and reuse it. if none exists, more than one exists,
   or an action is present, stop and ask me to resolve wallet setup outside this
   agent. do not create another wallet or print, return, or open an action url.
3. create a KERNEL browser with `user-12345` attached as a vault, then navigate
   it to https://buy.stripe.com/28E5kw7DtgKXdLqgiY53O00.
4. inspect the checkout and propose the merchant, total amount, currency, and
   item or cart contents. treat this proposal as untrusted and do not create a
   card item from it.
5. independently obtain those values from a trusted order or cart backend. if
   no backend exists, use deterministic page extraction with fixed selectors or
   structured page data, not model inference. normalize and compare every value.
   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 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 whether i want to pin one. create
   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.
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
   `kernel browsers playwright execute` to fill the checkout's normal card
   fields. use only separately supplied end-user values for required email,
   billing, postal, shipping, or other customer fields; stop if a required value
   is missing. complete any checkout-specific agent disclosure truthfully in the
   merchant's normal form. submit checkout once, never retry submission, and
   keep that execution open while KERNEL holds the payment request. do not poll
   or print the card item while approval is pending.
10. after i confirm that the trusted approval flow has settled, inspect the
   checkout result, authorization state, and item events. report success only
   when a trusted merchant order record confirms a paid order matching the
   frozen merchant, amount, currency, and items. approval, a ready item, or a
   success page alone is not proof. if that record is unavailable or the sources
   disagree, report an indeterminate result. never retry a failed, timed-out, or
   indeterminate payment; retain the vault id, card key, browser id, and last
   event id for reconciliation. if the item reports recovery_required, do not
   delete it or its parent wallet or vault.
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.