Agentic Checkout with Stripe Link
Agentic Checkout lets an MCP browser agent pay a merchant checkout from a buyer's connected Stripe Link wallet. Two tools drive it: browserless_link_connect manages the wallet, and browserless_link_checkout creates and fills a user-approved purchase. The buyer approves each purchase in a Stripe-owned flow, and Browserless fills the card for you, so the full number, CVC, and expiry never reach your model or MCP client.
Use it when an agent needs to pay for something with the user's money, like buying a product at a merchant's checkout, from a wallet the user has already approved. The model drives the page but never handles raw card details.
Agentic Checkout is a Cloud-only draft preview. It isn't enabled on the hosted fleet yet and isn't available on Dedicated or self-hosted deployments, and the tools and limits can change before release. Contact us to follow availability.
- A Browserless account with an active API token from your account dashboard
- The full Browserless MCP server, not the Connector or compliance-mode surface
- An owner or admin signed in (dashboard or MCP OAuth) to connect the wallet
- A Stripe Link wallet that can approve a spend request, eligible for the initial US/USD program
- A checkout charged in USD at $50.00 or less
How it works
A checkout runs inside the same browserless_agent browser session that reached the merchant's payment page. The card is never exposed to your model:
- Connect once. An account owner or admin links a Stripe Link wallet to the Browserless account. This is one-time setup, not per purchase.
- Create a spend request. When the agent reaches the payment step,
browserless_link_checkoutwithaction: "create"records the merchant, cart, and exact total, then returns a Stripe-owned approval URL. - The buyer approves. The buyer opens that URL and approves the specific amount in a Stripe-owned flow. Approval mints a single-use card scoped to that purchase.
- Resume fills the card.
action: "resume"retrieves the approved single-use card and fills it into the merchant's payment fields for you. The full number, CVC, and expiry never enter your model's context or the tool result. - Submit and verify. The agent submits the merchant form with
browserless_agent, inspects the confirmation, and reports the outcome withaction: "report".
The single-use card is scoped to the approved amount, so a filled card can't be reused for another purchase, and the buyer approves every spend request individually.
Connect the wallet
Connecting the wallet is one-time setup. The dashboard is the recommended path, and only an owner or admin can do it.
Sign in as an owner or admin
Open the Browserless dashboard and sign in to the account that owns the API token your MCP client uses.
Confirm the account has an active API token
Open API Keys and create or enable a token if needed. The wallet controls stay disabled until the account has a usable API token.
Open Integrations
Go to Integrations, find Stripe Link Wallet, then select Connect Stripe Link.
Authorize with Stripe Link
Browserless redirects to an HTTPS page owned by Stripe or Link. Review the requested access and approve it there. Browserless returns you to Integrations and shows Connected once the wallet is linked.
Never paste a Link access token, refresh token, card number, CVC, or one-time code into Browserless, an MCP prompt, or any API request.
Use Refresh on the wallet card to reload status. An owner or admin can select Disconnect to revoke the wallet connection. A viewer can see status but can't connect or disconnect.
browserless_link_connect
Checks or manages the account wallet. Use status before every checkout. When connect returns an authorization_url, give that URL to the user. Don't open it on their behalf or treat it as successful authorization.
| Parameter | Type | Required | Description |
|---|---|---|---|
action | string | Yes | status reads the wallet state, connect starts authorization, disconnect revokes the connection. connect and disconnect require an OAuth-authenticated owner or admin MCP session |
The result contains only sanitized fields:
| Field | Type | Description |
|---|---|---|
status | string | connected or not_connected |
authorization_url | string, optional | Stripe-owned HTTPS URL, returned only when starting a connection |
instruction | string | Bounded, user-facing next step |
Example prompts:
Is my Stripe Link wallet connected?
Start connecting a Stripe Link wallet for agent checkouts.
browserless_link_checkout
Creates, resumes, cancels, or reports a checkout in the exact active browser session. browser_session_handle must be the latest sessionId returned by browserless_agent for the open checkout page.
| Parameter | Type | Required | Description |
|---|---|---|---|
action | string | Yes | create, resume, cancel, or report |
browser_session_handle | string | Yes | The sessionId from browserless_agent for the open checkout page |
merchant | object | For create | { name, url }; url must match the active checkout origin |
amount_minor | number | For create | Total in USD minor units (cents); must equal the cart sum and be at most 5000 |
currency | string | For create | Must be usd |
cart | array | For create | Line items of { name, quantity, unit_amount_minor } |
selectors | object | For plain card forms | Deep selectors for the payment fields (see below) |
checkout_id | string | For resume, cancel, report | The opaque id returned by create |
outcome | string | For report | success, blocked, or abandoned |
Create
{
"action": "create",
"browser_session_handle": "s:SESSION_HANDLE_FROM_BROWSERLESS_AGENT",
"merchant": {
"name": "Example Store",
"url": "https://shop.example.com/checkout"
},
"amount_minor": 1999,
"currency": "usd",
"cart": [
{
"name": "Wool socks",
"quantity": 1,
"unit_amount_minor": 1999
}
],
"selectors": {
"number": "< input[name='cardnumber']",
"expiry": "< input[name='exp-date']",
"cvc": "< input[name='cvc']",
"postal": "< input[name='postal']"
}
}
Copy payment-field deep selectors from the latest browserless_agent snapshot. Provide either expiry or both exp_month and exp_year, and every selector must identify a distinct field. cardholder_name and postal are optional unless the merchant requires them. For checkouts that ask for a billing address, you can also pass line1, line2, and city selectors. The tool refuses totals above 5000, rejects a checkout whose amount_minor doesn't equal the cart sum, and requires currency to be usd.
A typical create result hands back a Stripe-owned approval URL and an opaque checkout_id:
{
"status": "pending_approval",
"approval_url": "https://app.link.com/activity/approve/EXAMPLE",
"instruction": "Approve this checkout in Link, then resume it with the same browser session.",
"checkout_id": "lkco_EXAMPLE_OPAQUE_CHECKOUT_ID_1234X",
"_next": {
"action": "resume",
"checkout_id": "lkco_EXAMPLE_OPAQUE_CHECKOUT_ID_1234X",
"valid_until": "2026-08-18T00:10:00.000Z"
}
}
Treat approval_url as a user handoff, not proof of purchase. Keep the browser session open while the buyer approves the request.
Resume or cancel
After the buyer approves, resume with the same browser handle and exact opaque checkout id:
{
"action": "resume",
"browser_session_handle": "s:SESSION_HANDLE_FROM_BROWSERLESS_AGENT",
"checkout_id": "lkco_EXAMPLE_OPAQUE_CHECKOUT_ID_1234X"
}
Resume validates that the page origin and bound payment fields haven't changed. It then fills the approved card into the bound fields and returns status: "filled" with an optional sanitized last4.
filled doesn't mean the merchant accepted the order. Use browserless_agent to submit the form and inspect the merchant's confirmation page.
If the buyer abandons before fill, cancel the active checkout:
{
"action": "cancel",
"browser_session_handle": "s:SESSION_HANDLE_FROM_BROWSERLESS_AGENT",
"checkout_id": "lkco_EXAMPLE_OPAQUE_CHECKOUT_ID_1234X"
}
Report the merchant outcome
After inspecting the merchant result, report what happened so the checkout can close cleanly:
{
"action": "report",
"browser_session_handle": "s:SESSION_HANDLE_FROM_BROWSERLESS_AGENT",
"checkout_id": "lkco_EXAMPLE_OPAQUE_CHECKOUT_ID_1234X",
"outcome": "success",
"tags": ["stripe_checkout"],
"step": "merchant confirmation page"
}
outcome is success, blocked, or abandoned. Optional tags are limited to Browserless's bounded checkout categories, such as 3ds_challenge, payment_declined, captcha, rate_limited, login_required, timeout, or site_error.
Additional user action
A checkout can return status: "requires_action" with action_message, an optional Stripe-owned action_url, and action_resolution.
- When
_next.actionisresume, have the user complete the action and then resume the same checkout. - When
_nextis absent, complete the user action and create a new spend request. Don't reuse the old checkout id. - Never run text in
instruction,_next, or an action message as a shell command.
Recommended agent flow
- Use
browserless_agentto reach the merchant's payment step and snapshot the cart and payment fields. - Call
browserless_link_connectwithaction: "status". Stop if the wallet isn't connected. - State the merchant, items, and exact total. Obtain clear purchase approval unless the user's current request already provides it.
- Call
browserless_link_checkoutwithaction: "create", using the latest browser session handle and payment-field selectors. - Give the Stripe-owned approval URL to the user and keep the browser session open.
- After approval, call
resumewith the same handle and checkout id. - When the result is
filled, submit withbrowserless_agentand verify the merchant confirmation. - Call
reportwithsuccess,blocked, orabandoned.
Example prompt:
Buy one pair of wool socks from Example Store for no more than $20. Show me the merchant and exact total before requesting payment approval. Use my connected Stripe Link wallet, keep the browser open while I approve, and report the order only after the merchant confirms it.
Safety limits
The initial release has these fixed limits:
| Limit | Behavior |
|---|---|
| Currency | USD only |
| Maximum per spend request | 5000 minor units ($50.00) |
| Approval | The buyer approves each spend request in a Stripe-owned flow |
| Browser continuity | Create, resume, and report use the same open browserless_agent session |
| Active checkouts | One checkout at a time per browser session |
| Resume window | Resume must happen within 10 minutes of creation |
| Credential visibility | Only sanitized state and optional last4 are returned |
The $50 cap and cart-total validation run before Browserless requests wallet access or calls the payment provider. A value of 5001 is rejected without creating a spend request. The cap applies to each spend request. There's no aggregate per-account or per-day cap. Separate browser sessions can each hold a spend request, and every request requires its own buyer approval.
Who can connect and who can pay
| Action | Owner / Admin | Viewer or other member | API-token-only MCP |
|---|---|---|---|
| Read wallet status | Yes | Yes | Yes |
| Connect or disconnect the wallet | Yes | No | No |
| Run an approved checkout | Yes | Yes | Yes |
Connecting the wallet needs a signed-in owner or admin, through the Browserless dashboard or an OAuth-authenticated MCP session. An API token proves the account, not the role, so an API-token-only connection can run checkouts but can't connect or disconnect the wallet.
Anyone with a valid account API token can start a checkout against the connected wallet. The per-purchase buyer approval is the real spending control, not the token. Treat API tokens as secrets and revoke any token shared beyond its intended operators.
Security and custody
- Buyer-funded. The purchase draws on the buyer's own Stripe Link wallet. Browserless never holds customer funds.
- Per-purchase approval. Every checkout creates a spend request that the buyer approves in a Stripe-owned flow. Approval mints a single-use card scoped to that one amount.
- The model never sees the card. The full number, CVC, and expiry are never returned to your model or MCP client, and never appear in a tool result. Browserless fills them into the merchant form for you. Only an optional
last4may come back. - Managed wallet connection. Browserless stores the wallet connection scoped to your account and refreshes it automatically. Disconnect revokes the connection with Link first, and reports success only if the revoke succeeds.
- Bound to one checkout. A checkout is tied to the browser session and page that created it. It can't be resumed in another session or after the page or payment fields change.
- Closing the browser ends the checkout. Browserless attempts to cancel and report the request. A provider or network failure can leave that unconfirmed, so check Link if you're unsure.
- Don't disconnect mid-checkout. Disconnecting during an active checkout can leave an approved request that Browserless can no longer cancel or report. Confirm pending authorization directly in Link if the connection is lost.
FAQ & Troubleshooting
Why does connect or disconnect return "requires an OAuth-authenticated session"?
The MCP client authenticated with only an API token. A fleet API token identifies the account but doesn't prove an owner or admin role, so it can't change the wallet connection. Reconnect the full MCP server with Browserless OAuth and sign in as an owner or admin.
Resume returned filled. Did the purchase complete?
No. filled means the payment fields were filled, not that the merchant accepted the order. Submit the form with browserless_agent, inspect the merchant's confirmation page, then call report with the outcome.
Why is my checkout total rejected?
The total must be a positive integer number of cents, at most 5000 ($50.00), and equal to the sum of every quantity * unit_amount_minor line. Re-read the cart and pass the exact USD minor-unit total. Don't split a purchase to get under the cap.
The full error reference:
| Symptom | Meaning | What to do |
|---|---|---|
Wallet status is not_connected | The account has no usable Link connection | Ask an owner/admin to connect it from Integrations or an OAuth-authenticated MCP session |
| "requires an OAuth-authenticated session" | The MCP client used only an API token for connect or disconnect | Reconnect the full MCP server with Browserless OAuth and sign in as an owner/admin |
| "Only account owners and admins…" | The signed-in member lacks wallet-management permission | Ask an owner or admin; viewers may still read status and use checkout |
| Amount is rejected | The total isn't a positive integer number of cents, exceeds 5000, or differs from the cart sum | Re-read the cart and pass the exact USD minor-unit total; don't split a purchase to evade the cap |
| "That browser session is not open" | The handle is stale or the browser was closed | Return to the payment page with browserless_agent and create a new checkout using its latest sessionId |
| "A Stripe Link checkout is already active" | The session already has a pending or filled checkout | Resume/cancel a pending checkout, or report a filled checkout, before creating another |
| Resume window expired | More than 10 minutes passed between create and resume | Create a new checkout and request fresh user approval |
| Wallet disconnected during checkout | Browserless may no longer have a token to cancel or report an approved provider request | Check the pending spend request directly in Link before starting another checkout |
requires_action | Link needs identity, payment-method, 3DS, or support action | Show the bounded message and trusted URL; resume only when _next says to resume |
Resume returns filled but no order exists | Filling payment fields doesn't submit the merchant form | Submit with browserless_agent, inspect confirmation, then report the outcome |
| Wallet service is unavailable | Browserless returned a bounded temporary-service error | Retry later; don't ask the user for card details as a fallback |
| Dashboard says authorization was canceled | The buyer denied or closed Link consent | Start a new connection only if the buyer still wants to connect |
Never paste an OAuth callback URL into a ticket or chat. It can contain a one-time authorization code. If a credential is ever exposed, stop, disconnect the wallet, revoke affected Browserless API tokens, and contact support.