Skip to main content
Take an agent from a product page to a placed order, charging the card once. Wiring it up takes about 20 minutes. A run takes a few minutes, plus any time the account holder spends on a prompt.

Before you start

  • An API key for the agent, with Fill a test card instead of my card cleared.
  • A card in the vault, governed by an agent card with the limits you want.
  • The Playwright SDK or the MCP server, connected to a proxy session.
  • The card’s tokens: listTokens() in the SDK, get_token_catalog over MCP.
This guide places a real order with the account holder’s real card. A key with Fill a test card instead of my card ticked fills a test card, but a real store can still record the order. To practice, use the practice checkout from the Quickstart.

When the account holder is involved

Four different prompts can pause a checkout. Know which one is waiting before you retry anything. Each prompt shows as a dashboard banner. It also goes out as an email, desktop notification or Background alerts notification on each channel the account holder turned on, but only while the prompt’s switch under Needs you is on: A purchase needs my approval (purchase approvals and new sites), A checkout needs my card security code or A checkout needs me to clear a bank or verification step. With the switch off, only the banner appears. Alerts are best effort. See Approvals and notifications.
A purchase approval happens automatically during the card fill. A handoff happens only when the agent asks for it. If an approval is waiting, requestChallengeHandoff() finds no challenge and does not help.

Run the checkout

1

Check the registry for the store

A store whose guest checkout is PayPal-only or wallet-only never takes a card.
If the account holder named a product but no store, buyableMerchants() lists stores that work, verified first, with dead ends removed.
Over MCP, send the host and path, never the raw page URL. The lookup is unauthenticated, and a hosted-checkout path (a Stripe cs_... path, a Shopify /checkouts/... path) carries a session token. The SDK’s recipesForUrl() strips it for you.
2

Snapshot the page before the first fill

After any token fill, snapshots and frame listings are refused and screenshots turn black. Look at the product page, cart and empty form now with page.snapshot(). Afterwards you have observe(), count() and detectChallenge(). See Redaction.
3

Dismiss the cookie or consent banner

It intercepts clicks. Click “Accept all”, “Accept” or “Got it” first.
4

Add to cart and confirm the cart filled

Add-to-cart often does nothing when clicked too early. Load /cart/, check for line items, and click again if it is empty.
5

Clear any delivery or region gate

Large retailers hide the buy button until a delivery location is set, and the browser starts with an empty profile every run. Fill the location box from the address tokens (av_addr_<label>_zip_<userId> and its _city and _country siblings). The registry reports a known gate as preconditions: ["delivery_location"].The first address fill on an unauthorized site can pause here for up to 15 minutes for a New site approval.
6

Set the country first

Country selects often default to the UK, which then rejects a US ZIP. Select the country (#billing_country on WooCommerce), wait for the page to refresh, then the state (#billing_state).
7

Fill billing from the card's billing address

Address verification compares billing with the card, and a mismatched postcode declines the charge. The token catalog does not say which address is a card’s billing address: TokenCatalogCard carries no address label or ID. Use the label the account holder gives you for that card, or ask them. An address typed while adding a card is labelled card1, card2 and so on, so do not assume a bill label exists.
8

Accept the terms by clicking the label

The real #terms checkbox is usually hidden. Click label[for="terms"] or label:has-text("I have read and agree").
9

Point at the total if the page names it oddly

If the proxy cannot read the total, a capped card is refused with checkout_total_unknown. Declare a hint on arrival: page.hint("order.total", { selector: ".summary__grand-total" }). See Page hints.
10

Fill the card in order

This step types the real card number into the store. Check the cart and the total first.
Expiry and billing first, then the card number, then the security code with nothing in between. See the card fill order. This is where a New site, purchase approval or live CVV prompt can reach the account holder.
11

Submit once

Never click pay or place-order a second time. A duplicate can charge twice. If the result is unclear, go to Confirm the order.
If a 3DS, OTP or CAPTCHA step appears, hand it over with Handle challenges.
12

Confirm the order

Over MCP, race a success selector against a failure selector with get_session_outcome, passing confirmsCheckout: true and the submitSelector you clicked. Then read get_run_report.In the SDK there is no race. Check the page yourself first: the success marker is present and the failure marker is absent. Only then call page.recordOutcome(), which records a confirmation as soon as you call it, even on an unconfirmed page. It accepts only an optional orderTotal. Then read browser.outcome().Either way, the merchant’s emailed receipt is the source of truth. See Confirming what happened.
Your agent can now pay at this store. Contribute a recipe so the next agent starts ahead. Per-platform steps for the supported platforms ship in the SDK as checkout-playbooks.md.

An agent checkout with the dashboard Activity page updating live.

The card fill order

Filling the card number checks the total against the account holder’s limits and, once approved, arms a one-shot authorization for the security code. The next action on the page spends it. Look at the page between the number and the code if you need to. Do anything that acts on the page before the number. One exception: a screenshot that returns securityCodeAuthorizationReleased has released it. Fill the number again, then the code. If expiry appears only after a number is typed, mark those controls cardField: true in fill_checkout and they move after the code. Any unmarked field between the number and the code gets the batch refused.

Stripe and other hosted card frames

After a fill, listFrames() and matching a frame by name or URL are refused with sensitive_data_blocked. Target the frame by CSS selector, and type with pressSequentially: Stripe Elements ignores value-only fills.
The legacy Card Element splits this into three frames (Secure card number input frame and its expiry and CVC siblings) with inputs cardnumber, exp-date and cvc. Probe each input with count() before you fill the number. If no selector reaches visible card fields, report the variant instead of submitting blind.

What the account holder sees

The account holder gets a dashboard banner. They also get a Review purchase email and a desktop or Background alerts notification on each channel they turned on, as long as their A purchase needs my approval switch is on. See Approvals and notifications. The approval page names the agent card, the merchant site and the amount, with a redacted screenshot.
Phone screen titled Approval needed, naming the agent card, the merchant site and the amount, with a redacted page screenshot and Approve and Deny buttons.Phone screen titled Approval needed, naming the agent card, the merchant site and the amount, with a redacted page screenshot and Approve and Deny buttons.

A purchase approval on the account holder's phone.

  • Approve: the fill continues. If the total changed since, the agent gets approval_denied and a retry asks again.
  • Deny: the agent gets approval_denied, and no real value is typed.
  • No answer: the agent gets approval_timeout. A retry asks again.
A live CVV prompt is a separate page. See Live CVV entry.

If something goes wrong

Everything else, including limit_reached and sensitive_data_blocked, is in Error codes and Troubleshooting. Next: Confirming what happened to prove the order landed before you report it.