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

# Enable Payments in a Browser Agent

> Give a browser agent provider-backed payment aliases and complete a web checkout without exposing card data

you're building a browser agent. you can add payments without bringing your application, agent, or browser into pci dss scope.

create a <span className="kernel-brand-name">KERNEL</span> vault and add a wallet backed by [stripe link](/docs/integrations/payments/stripe-link) or [agentcard](/docs/integrations/payments/agentcard). the provider-hosted flow collects and stores the user's payment method, so neither you nor your agent handles the card number or cvc. attach the vault when you create a browser session, give the agent the returned aliases, and let it complete a web checkout. <span className="kernel-brand-name">KERNEL</span> handles authorization and payment handoff at egress.

stripe link and agentcard are credential providers, not merchant payment
processors. at the browser form layer, both work with any web checkout that
accepts standard card details, and the merchant's processor does not need to be
stripe. end-to-end handoff also requires the outgoing request to match one of the
[native processor adapters](/docs/integrations/payments/overview#checkout-and-processor-coverage).

you can use either [stripe link](/docs/integrations/payments/stripe-link) or
[agentcard](/docs/integrations/payments/agentcard). choose based on how the payment
credential is created and when the user approves it:

|                 | stripe link                                                       | agentcard                                                                 |
| --------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------- |
| card source     | provider-minted, one-use card for a specific purchase             | user's enrolled card, represented by a reusable card item                 |
| approval timing | before the browser submits checkout                               | after the browser submits checkout and while the request is held          |
| reuse           | card item and aliases are consumed after the first native handoff | card item and aliases return to `ready`; every checkout requires approval |
| mode            | live only                                                         | deployment credential; the vault api doesn't expose sandbox or live mode  |

agentcard is backed by a card the user enrolls, but the agent and browser still
enter aliases rather than the underlying card details. those details remain
outside the browser and are handled during provider handoff at egress.

to run either path with a coding agent that has access to the <span className="kernel-brand-name">KERNEL</span> cli, use one of these prompts. connect the provider wallet first through your trusted application or a terminal outside the coding agent. the agent must stop whenever a provider action is required because cli output can contain the action url.

<CodeGroup>
  ```text stripe link theme={null}
  use the KERNEL cli to complete the following checkout with stripe link. 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 stripe 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. 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. 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 and never retry submission.
  10. inspect the checkout result and the card item's events. do not retry a
     failed, timed-out, or indeterminate payment. report the existing attempt so
     we can reconcile it first.
  ```

  ```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. do not retry a failed,
     timed-out, or indeterminate payment. report the existing attempt so we can
     reconcile it first.
  ```
</CodeGroup>

this guide starts after you have an existing browser agent. it changes how you prepare and pass payment input, not how your agent navigates or reasons about the checkout page.

## Before you start

* install a <span className="kernel-brand-name">KERNEL</span> sdk version that includes the `vaults` resource.
* set `KERNEL_API_KEY` and `KERNEL_PROJECT_ID` in the trusted controller that creates your browser.
* use a low-value web checkout you control whose outgoing payment request matches a [native processor adapter](/docs/integrations/payments/overview#checkout-and-processor-coverage). the merchant processor doesn't need to be stripe.
* stripe link card creation is live-only. agentcard mode comes from the integration's configured credential.
* for agentcard, keep an application-owned `AGENTCARD_MODE` deployment setting and fail closed unless it explicitly matches the sandbox or live environment you intend to use. the vault api does not return this mode.

<Warning>
  keep wallet collection and payment approval outside the agent. show hosted
  action urls only in a trusted user-facing surface. the only payment values you
  give the agent are the aliases returned in card state.
</Warning>

## 1. Create a vault

scope the client and vault to the same project that will own the browser session.

<CodeGroup>
  ```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
  ```
</CodeGroup>

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

## 2. Connect the end user's payment method and prepare a card item

`wallet` and `card` are KERNEL api resources, not the underlying wallet or card.
your end user initiates setup and purchases through your product. your
application then makes the authenticated api calls on their behalf. a card item
represents the payment authorization and aliases for a purchase; creating one
does not add or expose the end user's real card.

the end user creates the purchase intent. your application translates that
confirmed intent into a KERNEL card item without exposing its api key to the
user or agent.

| actor                    | responsibility                                                                                                                                        |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| your application         | enforces one wallet per provider; independently verifies purchase values; calls KERNEL; presents provider actions; observes state; attaches the vault |
| your end user            | initiates wallet setup and purchases; adds or selects the real payment method; confirms the verified purchase; completes approval                     |
| your browser agent       | proposes purchase values; after verification and confirmation, receives aliases, enters them into the checkout form, and submits once                 |
| KERNEL                   | creates the requested vault resources, returns aliases, enforces browser bindings, and handles payment data at egress                                 |
| the credential provider  | hosts wallet connection, enrollment, and approval; stores or mints the underlying payment credential                                                  |
| the merchant's processor | receives the recognized checkout request and returns its tokenization or payment response                                                             |

### Once per end user and provider

your payment settings ui must allow at most one wallet item for each provider in
a vault. list the vault's items before rendering provider controls, group wallet
items by `spec.provider`, and apply this flow:

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

if both stripe link and agentcard wallets exist, hide both provider add options.
recheck this condition in your trusted controller immediately before wallet
creation so a stale ui cannot submit a second wallet.

the api currently makes item keys unique, not wallet providers. using a
different key can create another wallet for the same provider, so your product
must enforce the one-per-provider rule. deleting a wallet invalidates its
dependent card items; require an explicit replacement flow rather than creating
a second wallet beside it.

1. your end user chooses a provider that does not already have a wallet in the vault.
2. your application creates or retrieves a vault for that end user, checks its items again, then creates a wallet item only when that provider is absent.
3. your application presents the returned action in a trusted user-facing surface outside the agent-controlled browser.
4. your end user completes the provider-hosted connection or enrollment flow.
5. your application waits until the wallet item's status is `connected`.

this is the point where your end user adds or connects their real payment
method. the end user does not create the KERNEL wallet item directly.

### Present hosted actions in your application

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

1. your backend 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 end 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

purchase verification is a required, fail-closed gate before creating or
updating a card item:

1. let the browser 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 trusted code 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 end 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.
7. for stripe link, list the connected wallet's payment methods and let the end user select one. for agentcard, pin an enrolled card or let the end user choose during approval.
8. ask KERNEL to prepare a card item on the end user's behalf. stripe link requires a new one-use item. agentcard can reuse a ready item and update its specification when the api permits. the item refers to the connected wallet; it is not the end user's real card.
9. for stripe link, invoke the advertised `authorize` operation using the same verified purchase object, then present any returned approval action to the end user. agentcard begins authorization after the browser submits checkout.
10. wait for the card item to become `ready`, then pass only its aliases to the browser agent.

<Warning>
  never create or authorize a card from values supplied only by the browser
  agent. end-user confirmation does not make an unverified proposal trustworthy.
  the independently verified purchase object must be the single source for the
  card specification, approval display, and authorization.
</Warning>

purchase verification doesn't depend on the merchant processor. prefer the
merchant's trusted order or cart backend. if one isn't available, use documented
structured checkout data or deterministic extraction for that checkout, and
fail closed when the values can't be independently verified.

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. when the checkout
response is your deterministic source, create the attached browser in step 3
before you create or update the card item, then inspect and submit with that same
browser session. the vault attachment covers items created later in that vault.

<Warning>
  don't start checkout until the wallet is `connected` and the card item is
  `ready`. 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.
</Warning>

<Note>
  we're adding credential-provider integrations and merchant-processor adapters
  and will update this guide as coverage expands. we plan to build additional
  card-collection and vault-management surfaces so developers can connect the
  credential providers and third-party vaults they already use. over time, we want{" "}
  <span className="kernel-brand-name">KERNEL</span> to support more of the
  browser-side steps required to complete a payment without exposing the
  underlying credential to the agent or browser.
</Note>

both credential-provider flows leave you with a card item whose `state.status` is `ready` and whose `state.aliases` contains `number`, `cvc`, `exp_month`, and `exp_year`.

### Collect non-card checkout fields

payment aliases cover only the card number, cvc, and expiry. merchant checkouts
can also require email, billing name, postal code, shipping address, phone
number, or other customer data. collect the required values from the end user in
your trusted application, or load values the end user has already approved from
your backend. pass them to the browser agent separately from the card aliases.
do not ask the agent to invent missing customer data.

merchant-specific agent disclosures are normal checkout fields. if the page
asks whether automation is acting for another person, instruct the agent to
answer truthfully in the merchant's form before submission. for a checkout that
offers **I am an AI agent acting on behalf of someone else**, select that option.
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. don't bypass the disclosure or replace the page's normal submission
with a raw processor request. after triggering submission once, never retry it,
including after a timeout, unchanged page, or indeterminate result.

retrieve the item immediately before creating the browser. don't cache aliases after an item expires, changes state, or is deleted.

<CodeGroup>
  ```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" ||
    !card.state.aliases
  ) {
    throw new Error(`payment item is ${card.state.status}`);
  }
  const aliases = card.state.aliases;
  ```

  ```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" or card.state.aliases is None:
      raise RuntimeError(f"payment item is {card.state.status}")
  aliases = card.state.aliases
  ```

  ```bash CLI theme={null}
  kernel vaults items get user-12345 notebook-order --wait 60 -o json
  ```
</CodeGroup>

## 3. Attach the vault to the browser

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

<CodeGroup>
  ```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
  ```
</CodeGroup>

`browser_live_view_url` lets the end user watch the checkout during confirmation
and agentcard approval pauses. `presentLiveView` represents your authenticated
application page: keep the url server-side with the end 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](/docs/browsers/live-view#embedding-in-an-iframe) for iframe and csp
requirements.

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

## 4. Give the aliases to your agent

pass the aliases as structured task input. instruct the agent to use them in the page's normal card fields and submit checkout once.

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

replace the angle-bracketed fields in your controller before sending the task. don't put oauth codes, action urls, provider responses, the vault api key, or the browser connection url in the prompt.

the agent can fill both top-level fields and payment fields embedded in iframes. let the merchant page's own submission code create the outgoing request. don't replace the checkout with a raw processor api call.

## 5. Keep approval and observation outside the agent

start observing the card item before the agent submits checkout and keep the observer running until the merchant reaches a terminal order state. this is required for agentcard, where the outgoing checkout request is held while the user approves it. the same observer works for stripe link and provides item events after credential substitution.

the example uses `presentProviderAction`, an application-owned function. it
must publish the action only to an authenticated session for the end user who
owns the vault. bind the action to that user, vault, and item; expire it no later
than the item or authorization expiry; and stop serving it as soon as the action
changes or disappears. don't put the url in application logs, analytics, model
context, or the agent-controlled browser.

<CodeGroup>
  ```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
  ```
</CodeGroup>

start the observer before checkout and cancel it only after the checkout
controller reaches a terminal merchant state or its reconciliation deadline.
the following application-owned functions stand in for your existing agent and
order backend:

<CodeGroup>
  ```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)
  ```
</CodeGroup>

if the merchant has not reached a terminal state by your controller's deadline,
classify the attempt as indeterminate, stop the observer, retain the attempt
identifiers, and do not submit checkout again. the observer presents user
actions and reads events; it does not submit checkout or repeat authorization.

each cli `--wait` performs one bounded observation, so rerun the commands to
continue observing the existing attempt. CLI commands prepare and observe the
payment state; they don't submit merchant payments or retry them. run `--open`
only from the trusted controller or a human-operated terminal, and never send
its output to the browser agent.

`events` returns an ordered array. an empty array means no new observation arrived during the wait. it does not mean payment failed. correlate `event.browser_id` with the browser you created when a vault is attached to more than one session.

## 6. Verify the outcome

use item state, item events, the checkout page, and the merchant's order record together.

| 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                                 |
| stripe link `consumed`, `credential_submitted`, or `credential_tokenized` | confirm processor and merchant state; credential use alone does not prove purchase success      |
| 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   |

use the merchant order record as the authority for whether the expected order
was created and paid. require its merchant, amount, currency, and items to match
the frozen purchase object. use authorization state and item events to explain
the provider and handoff path, and use the checkout page as supporting evidence.
only report success when the trusted merchant record confirms the matching
order. a ready agentcard item, an approved authorization, a delivered replay, or
a success page cannot establish that by itself. merchant success text is
page-specific: a completed stripe checkout might say **Thanks for your payment**,
but don't use that string or any other generic text matcher as the success
condition. when the sources disagree or the merchant record is unavailable,
keep the result indeterminate and do not retry.

<Warning>
  a timeout, browser deletion, missing event, or closed checkout does not undo
  provider execution or cancel a merchant order. don't retry automatically.
  retain the vault id, card key, browser id, and last event id until you have
  reconciled the attempt.
</Warning>

delete the browser when the task and outcome inspection are complete. keep or delete the vault and provider items according to whether the user needs them for a future task.
