How it works
- Intercept. The SDK attaches to the browser your Agent drives (Playwright or raw CDP) and intercepts requests from the page and every cross-origin iframe.
- Placeholder card. The SDK returns a public test card of the same brand as the user’s real card, such as Visa
4111 1111 1111 1111with the real card’s expiry. The Agent fills it into the checkout form. - Match. When the page sends a tokenization request to the PSP, the SDK matches it against reviewed PSP rules (host, path, and fields). It pauses the request only if the card fields equal the placeholder card issued for this checkout.
- Relay. The SDK sends only the request template, a placeholder marker, the
card_token, and allowlisted request headers to Clink Vault. - Substitute. Clink Vault checks the 30-minute payment window and the PSP rule, replaces the placeholder with the real card inside the PCI boundary, and calls the PSP directly.
- Replay. The SDK replays the PSP’s original status code, headers, and body to the page, which continues the order as with any normal payment.
Visible to the Agent and browser
- Card brand and last four digits
- A placeholder test card of the same brand
- Card setup links and payment status
- The PSP’s native response to the page
Only inside Clink's PCI boundary
- The real card number (PAN) and CVV, stored with KMS encryption
- Checks on the payment window and PSP rules
- The request sent to the PSP with the real card
- Card ciphertext for client-side encryption PSPs such as Adyen
Get started
Run the SDK on your application server or in a trusted checkout worker, never in the user’s browser.1
Install the SDK
2
Attach to the checkout page
Create a client, then attach it to the page your Agent drives. Pass
email so the SDK can start card setup when the user has no usable card.vault.attach(page) works with Playwright, locally or on a cloud browser through connectOverCDP, and covers cross-origin iframes. Without Playwright, such as over a cloud browser WebSocket, use vault.attachToCdp(cdp, sessionId, options). Both return the same CheckoutController.3
Call pay() until it returns a final status
Each
pay() call returns the next thing the Agent should do in next_action. Perform it, then call pay() again. fillCard and clickPayButton are your own page functions; see Code examples.4
Reconcile and detach
waitForOrderResult is your own page function: it polls readOrderResult until the page shows a result or the timeout passes. See card-form.ts in Code examples. This wait covers the user scanning a QR code or completing 3DS or a redirect after the loop stops at wait_user, and the order page loading after succeeded.reconcile() reads the merchant’s order result through resolveMerchantResult and reports it to Clink Vault. Always call detach() when checkout ends.If no card request reached the PSP, or
resolveMerchantResult is not set, reconcile() returns the current state unchanged. See Handle the result.checkout.getPaymentMethods() to see which methods the page supports. A card’s status is ready, needs_bind, or unsupported. QR wallets and PayPal on the page are needs_user: ask the user first, then start them with pay({ method }). See PSP coverage.
When the user needs to act
Vault does not use passkeys, SMS codes, or per-payment confirmation links. The user opens a card setup link once: they add a new card, or choose an existing card and re-enter its CVV. This opens a 30-minute payment window, during which the Agent completesfill_card → submit without the user. Clink hosts the card setup page; it requires no sign-in and can be used once.
When next_action.type is wait_user, its reason tells you what the user needs to do. The same action is also passed to onUserAction.
Notifications. Clink does not send the user SMS, email, or payment notifications. Your code delivers the card setup link through your own UI, messages, or notification channels. Do not let an LLM compose or rewrite the link.
pay(): call checkout.bind() during checkout, or vault.sessions.create({ email }) outside checkout. New cards and CVV re-entry use the same link. Call vault.cards.list({ customerId }) to see a user’s cards and when their windows expire.
Handle the result
Common
reason values with failed:
card_not_bound: No usable card exists, and noemailwas passed, so the SDK cannot start card setup.window_expired: The 30-minute payment window has expired, and noemailwas passed. Withemail,pay()returnswait_user(bind_card) instead, and the user selects this card and re-enters its CVV.card_frozenortoken_revoked: The card is frozen, or its card token has been revoked. Ask the user to set up another card withcheckout.bind().placeholder_mismatch: The page request contained a card number or CVV other than the placeholder card. The SDK refused to let it through.checkout_request_not_seen: No PSP request was intercepted within 30 seconds ofsubmit. Check that the pay button was actually clicked.session_expired: The card setup session failed or was locked. Callretry(), thenpay()to generate a new link. A link that has only expired does not cause this error;pay()generates a new link automatically.card_token_missingorcard_token_not_found: Card setup completed, but the session carried no card token, or the token is not in the card list. The SDK does not start card setup again; investigate the server-side data.merchant_declined: The PSP authorized the payment, butresolveMerchantResultreportedfailed, andreconcile()recorded it. Callretry()to try again.paypal_redirect_url_not_found: No PayPal approval link was found within 30 seconds (submitTimeoutMs) of clicking pay with PayPal.cancelled:cancel()ordetach()was called before the payment had a result.api_errorornetwork_error: A call to Clink Vault was rejected or could not reach the API, for example because the client credentials are invalid. Check the credentials and network, then try again.
unsupported:
rule_not_matchedorpsp_unsupported: The page’s PSP request matched no enabled rule. See PSP coverage.unsupported_card_brand: The user’s card brand has no placeholder card.card_encrypted: The page encrypted the card fields and sent them to an endpoint no rule covers, such as the merchant’s own server.cse_request_unsupported: An Adyen request the SDK does not relay, such as saving the card or paying with a stored card.
outcome_unknown:
pay_qr_unavailable: No usable QR code was found after submission. The merchant may have accepted the order, so do not switch to a card automatically.psp_timeout,missing_response, ormerchant_result_unknown: The PSP did not answer in time, Clink Vault returned no PSP response, orresolveMerchantResultreturnedunknownor threw. Verify the order before taking any other action.
PSP coverage
The SDK syncs matching rules from the server duringattach, so supporting a new standard PSP requires a rule update rather than a new SDK release. If rule sync fails because of a network error, timeout, 429, or 5xx, or returns a malformed response, the SDK falls back to its built-in rules. Any other error from Clink, such as 401 or 403, does not fall back: attach still succeeds but loads no rules, so a card request on the page returns unsupported (rule_not_matched). With invalid client credentials, pay() fails earlier with failed (api_error).
The SDK covers one-time payments with the placeholder card typed into the page. Leave any option on the merchant page to save the card turned off. If the page pays with a card the merchant already stores, that request carries no placeholder and does not go through Clink Vault.
- Token mode. The page sends the placeholder card in plain text, and Clink Vault substitutes the real card. Covers 18 PSPs with 80 built-in rules: Stripe, Checkout.com, Airwallex, Rapyd, Global Payments, Square, Authorize.net, Nuvei, EBANX, Mercado Pago, PayU, Razorpay, Mollie, Paysafe, Recurly, Bambora, Spreedly, and Shopify.
- CSE mode. For Adyen Sessions payments (
checkoutshopper-*.adyen.com), enabled. The SDK recognizes adyen-web JWE ciphertext and legacy securedFields 3.xadyenjs_0_1_*ciphertext. The server encrypts the real card with the Adyen public key and sends the request, so neither the page nor the Agent sees the ciphertext. - Not yet supported. Braintree, Worldpay, dLocal, Stripe Checkout Session confirm, Hosted Form, and merchant self-hosted Adyen. The SDK returns
unsupported, and the encrypted placeholder card is not sent.
pay() returns unsupported (unsupported_card_brand).
Besides cards, the SDK supports two payment methods that are completed entirely in the browser. They do not use Clink Vault authorization and send no card token. The page must already offer the method; the SDK does not create orders or generate QR codes.
- QR code payment (
paynow,wechat_pay,upi_qr,pix,bcmc_mobile). The SDK extracts that wallet’s own QR code from the page or an iframe, skipping logos and other wallets’ codes, and returns it as a PNG. For PayNow, the expiry is read from the QR code; other methods default to 10 minutes. Usereconcile()for the payment result. - PayPal link (
paypal). After the Agent selects PayPal and clicks pay, callpay({ method: 'paypal' })once to get the paypal.com approval link. What the user does on PayPal afterwards is outside the SDK.
fallback: 'card' applies only to QR code payments. The SDK falls back to a card only when resolveMerchantResult explicitly returns { status: 'failed', payment_confirmed_unpaid: true }. An unknown or pending result, or a QR code that expired locally, never triggers an automatic fallback.
Code examples
These examples run on your server. Merchant checkout pages differ, so adapt the selectors and page checks to the checkout your Agent automates.Complete checkout
checkout.ts runs one card payment from attach to reconcile. card-form.ts fills the placeholder card, clicks pay, and reads the merchant’s order result. loadVaultCard, saveVaultCard, notifyUser, and showQrCode are your application’s own functions.
Expose payment steps as Agent tools
When an LLM decides each step, give it tools that advance the payment rather than card values. The model sees only the status and the next step. Your code fills the card, andresolveMerchantResult reads the order result from the page, so the model cannot report a success that the merchant did not show.
agent-tools.ts
Use a cloud browser
The SDK does not launch or manage a browser. For a cloud browser, attach to the page your Agent already drives, then run the samepay() loop.