diff --git a/browsers/payments.mdx b/browsers/payments.mdx
index d04c7044..33af191a 100644
--- a/browsers/payments.mdx
+++ b/browsers/payments.mdx
@@ -27,7 +27,7 @@ agentcard uses an integration-specific alternative: the agent enters non-sensiti
5. invoke KERNEL's advertised `fill` operation to inject payment fields without supplying their values, then inspect the outcomes before submitting. for agentcard's alias-based flow, enter aliases and keep a trusted approval observer running while the checkout request is held.
6. submit checkout once, complete any remaining provider-hosted approval, and reconcile with the merchant's order state. fill or approval alone is not payment success.
-`wallet` and `card` are vault item types. the wallet supplies the provider connection; the card represents payment material and its authorization state. retrieve the item and check `available_operations` before invoking an operation. don't assume every item supports `fill`.
+`wallet` and `card` are vault item types. the wallet supplies the provider connection; the card represents payment material and its authorization state. where enabled for your organization, link and KERNEL purchases can also be `payment_request` items, which work like card items; agentcard purchases are always `card` items. retrieve the item and check `available_operations` before invoking an operation. don't assume every item supports `fill`.
```mermaid
%%{init: {"flowchart": {"nodeSpacing": 24, "rankSpacing": 28, "padding": 12}}}%%
@@ -56,6 +56,7 @@ your application must independently verify the merchant, items, amount, and curr
- [choose a wallet integration](/integrations/wallets/overview) for payment-method connection, approval, and reuse options.
- [KERNEL card](/integrations/wallets/kernel) to collect a user's card directly, with hosted or embedded enrollment.
+- [KERNEL payment methods](/integrations/wallets/kernel-payment-methods) to hold several cards in one KERNEL wallet and choose which card pays.
- [enable payments in a browser agent](/browsers/enable-payments-in-browser-agent) for the controller, agent, and checkout walkthrough.
- [fill browser fields](/vaults/fill) for KERNEL's injection api, field selection, and outcome contract.
- [pricing](/info/pricing) for vault limits and payment charges.
diff --git a/docs.json b/docs.json
index 93054184..7d484be0 100644
--- a/docs.json
+++ b/docs.json
@@ -213,6 +213,7 @@
"browsers/payments",
"integrations/wallets/overview",
"integrations/wallets/kernel",
+ "integrations/wallets/kernel-payment-methods",
"integrations/wallets/stripe-link",
"integrations/wallets/agentcard",
"integrations/wallets/embedded-card-capture",
diff --git a/info/api-key-permissions.mdx b/info/api-key-permissions.mdx
index 0567f3b0..6d27711c 100644
--- a/info/api-key-permissions.mdx
+++ b/info/api-key-permissions.mdx
@@ -64,6 +64,7 @@ Resources must be inside the new key's organization, or its project for a projec
| Any operation on an existing browser | The browser action, plus `profiles:use` on its attached profile and `vaults:use` on every linked vault. If the browser saves profile changes, `profiles:write` on that profile too. |
| Update a browser | `browsers:write` and `browsers:use`, plus `use` on any profile, vault, or proxy the update adds. |
| Fill from a vault | `vaults:use` on the vault and `browsers:use` on the browser. |
+| Add or remove a card in a Kernel wallet (`add_payment_method`, `remove_payment_method`) | `vaults:use` and `vaults:write` on the vault. Without `vaults:write`, the wallet doesn't list either operation in `available_operations`, and invoking one returns `403` with `insufficient_scope`. Other wallet and purchase operations need only `vaults:use`. |
| Run a proxy health check | `proxies:use` and `proxies:read`. |
List endpoints return only the objects the key can access, so a key without a matching grant gets an empty list.
diff --git a/integrations/wallets/embedded-card-capture.mdx b/integrations/wallets/embedded-card-capture.mdx
index 7967c003..4a1b3c77 100644
--- a/integrations/wallets/embedded-card-capture.mdx
+++ b/integrations/wallets/embedded-card-capture.mdx
@@ -33,9 +33,13 @@ bun add @onkernel/vault-react
the package requires react 18 or later and ships no styles.
-## Create the wallet on your backend
+## Get a capture token on your backend
-create the wallet from your authenticated backend, then read `action.capture_token` from the `card_enrollment` action. the token is a secret capability for this one wallet, not an api key.
+read `capture_token` from a `card_enrollment` action in your authenticated backend. the token is a secret capability for one card enrollment, not an api key. where the action comes from depends on whether your organization has [payment methods](/integrations/wallets/kernel-payment-methods) enabled.
+
+### Wallet with one card
+
+create the wallet, then read `action.capture_token` from the wallet's `card_enrollment` action.
@@ -75,6 +79,40 @@ if wallet.action is not None and wallet.action.name == "card_enrollment":
the wallet starts in `pending_authorization`. after the cardholder adds a card, it becomes `connected`.
+### Wallet with payment methods
+
+where payment methods are enabled, the wallet starts `connected` with no card and no action. invoke `add_payment_method` for each card, then read `payment_method.action.capture_token` from the result.
+
+
+
+```typescript TypeScript
+const added = await kernel.vaults.items.performOperation("card-wallet", {
+ id_or_name: "user-12345",
+ type: "add_payment_method",
+});
+
+if (added.type === "add_payment_method") {
+ // return this to the cardholder's page only
+ const captureToken = added.payment_method.action?.capture_token;
+}
+```
+
+```python Python
+added = kernel.vaults.items.perform_operation(
+ "card-wallet",
+ id_or_name="user-12345",
+ type="add_payment_method",
+)
+
+if added.type == "add_payment_method" and added.payment_method.action is not None:
+ # return this to the cardholder's page only
+ capture_token = added.payment_method.action.capture_token
+```
+
+
+
+the new payment method starts in `pending_enrollment`. after the cardholder adds a card, it becomes `active`.
+
## Render the component
deliver the token to the intended cardholder's page through your authenticated application, then render `KernelCardCapture`:
@@ -110,7 +148,7 @@ export function AddCard({ captureToken }: { captureToken: string }) {
| prop | description |
| ------------- | ------------------------------------------------------------------------------------------------------------ |
-| `token` | the wallet's `action.capture_token` |
+| `token` | the `capture_token` from the wallet's or payment method's `card_enrollment` action |
| `baseUrl` | api origin; defaults to `https://api.onkernel.com` |
| `className` | added to the root element, in both the form and completion views |
| `fieldCss` | css applied inside the card number, expiry, and cvc iframes |
@@ -244,7 +282,7 @@ wallet = kernel.vaults.items.retrieve(
-a `connected` wallet has a stored card. inspect the wallet's `payment_methods` before creating a purchase.
+for a wallet with one card, `connected` means the card is stored. a wallet with payment methods is `connected` even before its first card, so retrieve it with the `payment_methods` expansion and check that the payment method you added is `active`. in both cases, inspect the wallet's `payment_methods` before creating a purchase.
## Handle errors
@@ -266,7 +304,7 @@ after `expired`, `unavailable`, or `unconfirmed`, the component removes the form
## Token lifetime
-a `capture_token` lasts 30 minutes. when it expires, retrieve the wallet again from your backend; KERNEL returns a new `card_enrollment` action with a new `capture_token`. changing the component's `token` prop resets it.
+a `capture_token` lasts 30 minutes. when it expires on a wallet with one card, retrieve the wallet again from your backend; KERNEL returns a new `card_enrollment` action with a new `capture_token`. for a wallet with payment methods, invoke `add_payment_method` again for a new payment method and token. changing the component's `token` prop resets it.
if the cardholder leaves the page after creating a card but before KERNEL confirms it, the component makes one best-effort attempt to resend the card as it unmounts or the page unloads. it ignores that request's result, so the card can still go unrecorded. always confirm the wallet's state from your backend, as described in [handle completion](#handle-completion).
diff --git a/integrations/wallets/kernel-payment-methods.mdx b/integrations/wallets/kernel-payment-methods.mdx
new file mode 100644
index 00000000..96d87b75
--- /dev/null
+++ b/integrations/wallets/kernel-payment-methods.mdx
@@ -0,0 +1,340 @@
+---
+title: "KERNEL Card Payment Methods"
+sidebarTitle: "KERNEL Payment Methods"
+description: "Hold several cards in one KERNEL wallet and choose which card pays"
+---
+
+a [KERNEL wallet](/integrations/wallets/kernel) can hold zero or more cards. each card is a payment method scoped to its wallet: you add cards with `add_payment_method`, list them with the `payment_methods` expansion, choose one per purchase with `payment_method_id`, and remove them with `remove_payment_method`.
+
+
+ payment methods and `payment_request` items are enabled per organization and
+ are off by default. [contact support](/info/support) to enable them for your
+ organization.
+
+ without payment methods, a KERNEL wallet holds one card: it starts in
+ `pending_authorization` with a `card_enrollment` action and becomes
+ `connected` once the card is stored. that card is the wallet's default
+ payment method, with the id `card`. follow [KERNEL card](/integrations/wallets/kernel)
+ for that flow.
+
+
+## Create an empty wallet
+
+with payment methods enabled, creating a KERNEL wallet returns a `connected` wallet with no cards and no `action`. the wallet stays `connected` before its first card and after its last card is removed, so `connected` doesn't mean the wallet can pay. add a card, then check the card's status and eligibility.
+
+a connected wallet advertises `add_payment_method` and, once it holds a card, `remove_payment_method` in `available_operations`. invoke them only when they're advertised.
+
+
+
+```typescript TypeScript
+import Kernel from "@onkernel/sdk";
+
+const kernel = new Kernel({
+ projectID: process.env.KERNEL_PROJECT_ID!,
+ maxRetries: 0,
+});
+const vault = await kernel.vaults.upsert({ name: "user-12345" });
+
+const wallet = await kernel.vaults.items.upsert("card-wallet", {
+ id_or_name: vault.id,
+ type: "wallet",
+ spec: { provider: "kernel" },
+});
+
+const canAddCard = wallet.available_operations.some(
+ ({ type }) => type === "add_payment_method",
+);
+```
+
+```python Python
+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")
+
+wallet = kernel.vaults.items.upsert(
+ "card-wallet",
+ id_or_name=vault.id,
+ type="wallet",
+ spec={"provider": "kernel"},
+)
+
+can_add_card = any(
+ operation.type == "add_payment_method" for operation in wallet.available_operations
+)
+```
+
+
+
+wallets created before payment methods were enabled keep their card as the default payment method `card` and can add more cards.
+
+## Add a card
+
+`add_payment_method` creates a payment method in `pending_enrollment` and returns it with a single-use `card_enrollment` action and an `expires_at` 30 minutes later. each call creates a separate link for one card.
+
+
+
+```typescript TypeScript
+const added = await kernel.vaults.items.performOperation("card-wallet", {
+ id_or_name: vault.id,
+ type: "add_payment_method",
+});
+if (added.type !== "add_payment_method") {
+ throw new Error("unexpected operation response");
+}
+
+const paymentMethod = added.payment_method;
+// show paymentMethod.action.url to the cardholder in your trusted ui, not to the agent
+console.log(paymentMethod.id, paymentMethod.status, paymentMethod.expires_at);
+```
+
+```python Python
+added = kernel.vaults.items.perform_operation(
+ "card-wallet",
+ id_or_name=vault.id,
+ type="add_payment_method",
+)
+if added.type != "add_payment_method":
+ raise RuntimeError("unexpected operation response")
+
+payment_method = added.payment_method
+# show payment_method.action.url to the cardholder in your trusted ui, not to the agent
+print(payment_method.id, payment_method.status, payment_method.expires_at)
+```
+
+
+
+share `action.url` privately with the cardholder, for example on an authenticated page in your application. the url is a capability for this one enrollment: don't send it to the agent, log it, or share it with anyone else. to collect the card on your own page instead, pass `action.capture_token` to [embedded card capture](/integrations/wallets/embedded-card-capture).
+
+the card goes directly to the card vault; KERNEL never accepts card details through this api. if enrollment fails, the link accepts another card until it expires. an expired link doesn't affect the wallet's other cards; call `add_payment_method` again for a new link.
+
+a wallet holds at most 10 payment methods, including pending ones, and at most 3 open enrollment links. at either limit, the wallet stops advertising `add_payment_method`, and invoking it returns `409` with the reason. remove a card or let an open link expire before adding another.
+
+## List payment methods
+
+retrieve the wallet with the `payment_methods` expansion to list its cards in creation order. each method includes `id`, `status`, `is_default`, `display`, and `capabilities.single_use_card`, with `eligible` and `reasons`. a pending method also includes its `action` and `expires_at`.
+
+
+
+```typescript TypeScript
+const current = await kernel.vaults.items.retrieve("card-wallet", {
+ id_or_name: vault.id,
+ expand: ["payment_methods"],
+});
+if (current.type !== "wallet") {
+ throw new Error("unexpected item type");
+}
+
+const methods = current.expanded?.payment_methods ?? [];
+for (const method of methods) {
+ console.log(
+ method.id,
+ method.status,
+ method.display.last4,
+ method.capabilities.single_use_card?.eligible,
+ );
+}
+```
+
+```python Python
+current = kernel.vaults.items.retrieve(
+ "card-wallet",
+ id_or_name=vault.id,
+ expand=["payment_methods"],
+)
+if current.type != "wallet":
+ raise RuntimeError("unexpected item type")
+
+methods = (current.expanded.payment_methods or []) if current.expanded else []
+for method in methods:
+ single_use_card = method.capabilities.single_use_card
+ print(
+ method.id,
+ method.status,
+ method.display.last4,
+ single_use_card.eligible if single_use_card else None,
+ )
+```
+
+
+
+| status | meaning | what to do |
+| --- | --- | --- |
+| `pending_enrollment` | an enrollment link is open | wait for the cardholder, or let the link expire |
+| `active` | the card is stored | pay with it when `capabilities.single_use_card.eligible` is `true` |
+| `removal_pending` | removal started but its outcome is unknown | retry `remove_payment_method` later |
+| `recovery_required` | the enrollment outcome is unknown, or the stored card can't be read | check `status_reason`: remove a method with an unknown enrollment outcome; [contact support](/info/support) for a card that can't be read, since removal returns `409` |
+
+a method that isn't `active` is never eligible to pay. `is_default` is `true` only for the default card `card`; wallets created with payment methods enabled have no default card.
+
+## Choose a card for a purchase
+
+pass the chosen method's `id` as `spec.payment_method_id` when you create a purchase. the purchase is bound to that card when it's created: later operations can't switch it to another card or wallet, and an update that changes either returns `409`. to pay with a different card, create a new purchase.
+
+KERNEL purchases can be `card` or `payment_request` items:
+
+| item type | `payment_method_id` |
+| --- | --- |
+| `card` | optional. omitting it selects the default card `card` and never falls back to another card. if the wallet has no default card, creation returns `400` and you must set the id. |
+| `payment_request` | required. must name an `active` method that's eligible to pay. |
+
+`payment_request` takes the same spec as a KERNEL `card` item, except that `payment_method_id` is required, and supports the same operations, including `authorize`, `fill`, and `confirm_transaction`. neither type is deprecated, and `card` items keep working. updates to a `payment_request` item must set `type: "payment_request"`.
+
+
+
+```typescript TypeScript
+const card = methods.find(
+ (method) =>
+ method.status === "active" &&
+ method.capabilities.single_use_card?.eligible === true,
+);
+if (!card) {
+ throw new Error("add an eligible card first");
+}
+
+let purchase = await kernel.vaults.items.upsert("notebook-order", {
+ id_or_name: vault.id,
+ type: "payment_request",
+ spec: {
+ provider: "kernel",
+ wallet: "card-wallet",
+ payment_method_id: card.id,
+ amount: 2306,
+ currency: "usd",
+ merchant_name: "example shop",
+ merchant_url: "https://shop.example.com/checkout",
+ merchant_country: "US",
+ },
+});
+
+const authorized = await kernel.vaults.items.performOperation(purchase.key, {
+ id_or_name: vault.id,
+ type: "authorize",
+});
+if (authorized.type !== "payment_request") {
+ throw new Error("unexpected operation response");
+}
+// visa: show authorized.action.url (spend_approval) to the cardholder, then wait for ready
+purchase = await kernel.vaults.items.retrieve(purchase.key, {
+ id_or_name: vault.id,
+ wait: 60,
+});
+```
+
+```python Python
+card = next(
+ (
+ method
+ for method in methods
+ if method.status == "active"
+ and method.capabilities.single_use_card is not None
+ and method.capabilities.single_use_card.eligible
+ ),
+ None,
+)
+if card is None:
+ raise RuntimeError("add an eligible card first")
+
+purchase = kernel.vaults.items.upsert(
+ "notebook-order",
+ id_or_name=vault.id,
+ type="payment_request",
+ spec={
+ "provider": "kernel",
+ "wallet": "card-wallet",
+ "payment_method_id": card.id,
+ "amount": 2306,
+ "currency": "usd",
+ "merchant_name": "example shop",
+ "merchant_url": "https://shop.example.com/checkout",
+ "merchant_country": "US",
+ },
+)
+
+authorized = kernel.vaults.items.perform_operation(
+ purchase.key,
+ id_or_name=vault.id,
+ type="authorize",
+)
+# visa: show authorized.action.url (spend_approval) to the cardholder, then wait for ready
+purchase = kernel.vaults.items.retrieve(purchase.key, id_or_name=vault.id, wait=60)
+```
+
+
+
+approval, fill, and network-token behavior are the same as for a single card. see [make a purchase](/integrations/wallets/kernel#make-a-purchase).
+
+## Check the card funding a purchase
+
+a KERNEL purchase reports the card it's bound to as `state.payment_method_id` and whether that card can still pay as `state.funding_status`:
+
+| `funding_status` | meaning |
+| --- | --- |
+| `available` | the card can fund this purchase |
+| `unavailable` | the card is being removed or awaits recovery. authorization, fill, and confirmation are refused until it recovers. |
+| `removed` | the card was removed from its wallet. this purchase can't be paid; create a new purchase with another card. |
+
+`funding_status` never changes the purchase's `status`, which still reports what already happened to the purchase. purchases created before cards could be selected report the default card `card`.
+
+## Remove a card
+
+`remove_payment_method` deletes the stored card and blocks new purchases, fills, and confirmations funded by it. the wallet's other cards aren't affected, and the wallet stays `connected` even with no cards left.
+
+
+
+```typescript TypeScript
+const removed = await kernel.vaults.items.performOperation("card-wallet", {
+ id_or_name: vault.id,
+ type: "remove_payment_method",
+ payment_method_id: "zk3o8w2h0a5n4e9q1x7c6v2b",
+});
+if (removed.type === "remove_payment_method") {
+ console.log(removed.payment_method_id, removed.status); // "removed"
+}
+```
+
+```python Python
+removed = kernel.vaults.items.perform_operation(
+ "card-wallet",
+ id_or_name=vault.id,
+ type="remove_payment_method",
+ payment_method_id="zk3o8w2h0a5n4e9q1x7c6v2b",
+)
+if removed.type == "remove_payment_method":
+ print(removed.payment_method_id, removed.status) # "removed"
+```
+
+
+
+| response | meaning | what to do |
+| --- | --- | --- |
+| `409` `purchase_unresolved` | a purchase funded by this card has an unresolved outcome | reconcile that purchase before removing the card |
+| `409` `provider_revocation_pending` | the card vault's deletion outcome is uncertain. the method stays `removal_pending` and refuses payments. | retry the removal later |
+| `429` `provider_rate_limited` | the payment provider is rate limiting requests | wait, then retry |
+
+removal isn't proof that a payment didn't happen. reconcile any purchase with the merchant's order record before treating it as unpaid.
+
+## Permissions
+
+`add_payment_method` and `remove_payment_method` create and delete stored cards, so a [scoped api key](/info/api-key-permissions) needs `vaults:write` in addition to `vaults:use` on the vault. a key without `vaults:write` doesn't see either operation in `available_operations`, and invoking one returns `403` with `insufficient_scope`. other wallet and purchase operations need only `vaults:use`.
+
+## Events
+
+the wallet's [item events](/vaults/overview#api-behavior) record each card's lifecycle, with the card's id in `data.payment_method_id`:
+
+| event | recorded when |
+| --- | --- |
+| `payment_method_enrollment_started` | `add_payment_method` opens an enrollment link |
+| `payment_method_enrolled` | a card added with `add_payment_method` is stored |
+| `payment_method_removed` | removal finishes |
+| `payment_method_recovery_required` | the card needs recovery |
+
+each new purchase records `purchase_created` with `item_type`, `provider`, and `payment_method_id_present`.
+
+## Reference
+
+- [vault items api](https://kernel.sh/docs/api-reference/vaults/create-or-retrieve-an-identical-vault-item-by-immutable-key): request fields, operations, and expiry.
+- [KERNEL card](/integrations/wallets/kernel): purchase approval, fill, and network tokens.
+- [embedded card capture](/integrations/wallets/embedded-card-capture): collect a card on your own page.
diff --git a/integrations/wallets/kernel.mdx b/integrations/wallets/kernel.mdx
index 848e32db..2c1dbf57 100644
--- a/integrations/wallets/kernel.mdx
+++ b/integrations/wallets/kernel.mdx
@@ -5,6 +5,13 @@ description: "Collect a user's card and use it for browser purchases"
use `provider: "kernel"` to collect a user's existing card for browser purchases. KERNEL manages the provider connection; you don't need your own vgs account.
+
+ this page covers a wallet with one card, which every organization gets by
+ default. where [payment methods](/integrations/wallets/kernel-payment-methods)
+ are enabled, a new wallet starts empty and `connected`, and you add each card
+ with `add_payment_method` instead of the wallet's `card_enrollment` action.
+
+
## Connect a card
1. create a [vault](/vaults/overview) and a `wallet` item with `spec.provider: "kernel"`.
@@ -60,7 +67,7 @@ card details go directly to very good security (vgs) through hosted fields. vgs
## Make a purchase
1. attach the vault when you [create the browser](/vaults/overview#scope-and-attachment).
-2. create a `card` item for the purchase, referencing the wallet and the merchant, amount, and currency. visa cards also require `merchant_country`, the merchant's two-letter country code.
+2. create a `card` item for the purchase, referencing the wallet and the merchant, amount, and currency. visa cards also require `merchant_country`, the merchant's two-letter country code. without `spec.payment_method_id`, the purchase uses the wallet's default card, `card`; to choose another card, see [payment methods](/integrations/wallets/kernel-payment-methods#choose-a-card-for-a-purchase).
3. invoke the advertised `authorize` operation and complete any returned user action. keep action urls in your trusted user interface, outside the agent's context.
4. when `fill` appears in `available_operations`, [fill the checkout](/vaults/fill), submit once, and verify the merchant's order record. a completed fill isn't proof of payment.
@@ -147,6 +154,8 @@ for eligible cards, checkout receives a network token number, expiry, and one-ti
| visa | requires `spec.merchant_country`; complete the returned `spend_approval` action, including any required verification |
| supported mastercard | no hosted purchase approval |
+the purchase's `state.payment_method_id` names the card it's bound to, and `state.funding_status` reports whether that card can still pay. see [check the card funding a purchase](/integrations/wallets/kernel-payment-methods#check-the-card-funding-a-purchase).
+
there's no general standing approval or spending-limit setting across these paths. your application must verify purchase details before authorization. complete any returned user action and fill before the purchase's `expires_at`.
diff --git a/integrations/wallets/overview.mdx b/integrations/wallets/overview.mdx
index 25873a85..1a99946b 100644
--- a/integrations/wallets/overview.mdx
+++ b/integrations/wallets/overview.mdx
@@ -17,7 +17,7 @@ see [how payments work on KERNEL](/browsers/payments#how-payments-work) for the
| integration | use it to | environment |
| --- | --- | --- |
-| [KERNEL card](/integrations/wallets/kernel) | collect a user's card directly through hosted or embedded enrollment | live only |
+| [KERNEL card](/integrations/wallets/kernel) | collect a user's card directly through hosted or embedded enrollment; where enabled, hold several cards as [payment methods](/integrations/wallets/kernel-payment-methods) | live only |
| [link by stripe](/integrations/wallets/stripe-link) | use payment methods from the user's link wallet; requires a us phone number | live only |
| [agentcard](/integrations/wallets/agentcard) | use agentcard's card vault and provider-executed checkout | sandbox or live, set by the agentcard credential |
@@ -68,7 +68,14 @@ has a wallet in any state, and reuse or recover the existing item.
a card's `spec.wallet` must reference a wallet in the same vault and from the
same provider.
-for provider-specific behavior, see [KERNEL card](/integrations/wallets/kernel), [link](/integrations/wallets/stripe-link#item-reference), or [agentcard](/integrations/wallets/agentcard#item-reference).
+link and KERNEL purchases can also be `payment_request` items where they're
+enabled for your organization; [contact support](/info/support) to enable them.
+a `payment_request` takes the same spec as the provider's `card` item and
+supports the same operations. KERNEL payment requests must set
+`payment_method_id`. agentcard purchases stay `card` items. updates to a
+`payment_request` item must set `type: "payment_request"`.
+
+for provider-specific behavior, see [KERNEL card](/integrations/wallets/kernel), [KERNEL payment methods](/integrations/wallets/kernel-payment-methods), [link](/integrations/wallets/stripe-link#item-reference), or [agentcard](/integrations/wallets/agentcard#item-reference).
## Payment actions and recovery
diff --git a/vaults/overview.mdx b/vaults/overview.mdx
index 63824c9b..767c594b 100644
--- a/vaults/overview.mdx
+++ b/vaults/overview.mdx
@@ -109,7 +109,7 @@ is required for browser operations, including `fill`.
| resource | technical behavior |
| ------------------ | --------------------------------------------------------------------------------------------------- |
| vault | resource with an immutable `name` that groups typed items |
-| item | typed resource addressed by an immutable `key`: `credential`, `wallet`, or `card` |
+| item | typed resource addressed by an immutable `key`: `credential`, `wallet`, `card`, or `payment_request` |
| alias | non-sensitive, format-valid stand-in returned in state for eligible items; aliases belong to that item |
| action | user interaction returned as `action`, such as a hosted collection or approval url |
| operation | api action advertised in `available_operations`, such as `collect`, `fill`, `webmcp_invoke`, `authorize`, `prepare_checkout`, or a 1Password `1pw_*` operation, when eligible |
@@ -200,7 +200,7 @@ see the [vaults api reference](https://kernel.sh/docs/api-reference/vaults/creat
## Payment items
-payment items use the same vault resource model: a `wallet` connects a payment method through a provider, and a `card` represents payment material and authorization state. KERNEL's `fill` api injects values from supported cards without returning them to your application or model. agentcard uses an alias-based alternative.
+payment items use the same vault resource model: a `wallet` connects a payment method through a provider, and a `card` represents payment material and authorization state. where enabled, link and KERNEL purchases can also be `payment_request` items, which work like card items. KERNEL's `fill` api injects values from supported cards without returning them to your application or model. agentcard uses an alias-based alternative.
see [payments on KERNEL](/browsers/payments) for the offering and lifecycle, then [choose a wallet integration](/integrations/wallets/overview).