# Enable agents to spend

Let your agent retrieve one-time-use payment credentials to transact online.

Agent payments are available to US and Canadian customers. The sellers your customers buy from can be outside those countries.

A spend request is your agent’s request to spend a specific amount at a specific seller. Your agent creates a spend request, the customer approves it, and Link returns a one-time-use payment credential for that purchase. The customer’s real card or bank number is never exposed.

Each spend request represents one intent to purchase from the customer. You can create many spend requests per customer as they ask your agent to make different purchases.

Before you begin, make sure that you’ve [authorized your agent](https://docs.stripe.com/agentic-commerce/agents/link-agent-wallet/oauth.md) with the `payment_methods.agentic` scope.

## How it works 

1. Your agent evaluates how the seller takes payment, which determines the credential type needed.
2. Your agent creates a spend request with the amount, the seller, and a description of the purchase.
3. The customer approves it on the Link website or in the mobile app.
4. Your agent polls until the request is approved or denied.
5. Your agent retrieves the credential and pays the seller.

## Integration checklist

To get the best experience for your customers and your agent, follow these recommendations.

- [ ] Use idempotency keys
      When creating a spend request, provide an idempotency key that represents that purchase intent. Your agent can safely retry a spend request if it encounters any errors.

      - [Idempotency keys](https://docs.stripe.com/agentic-commerce/agents/link-agent-wallet/use-link-wallet-pay-online.md#idempotency-keys)

- [ ] Use WebMCP with Stripe-enabled checkouts
      If your agent needs to purchase from a Stripe enabled checkout, it doesn’t need to use a virtual card. Use a Link Pay Token instead to instantly complete the entire checkout.

      - [Link Pay Tokens](https://docs.stripe.com/agentic-commerce/agents/link-agent-wallet/use-link-wallet-pay-online.md#pay-with-a-link-pay-token)

- [ ] Handle payment errors
      If Link encounters any errors or issues while making a payment, you can see the details back on the spend request. Look for the `next_action` block to handle errors and retry.

      - [Handle next actions](https://docs.stripe.com/agentic-commerce/agents/link-agent-wallet/use-link-wallet-pay-online.md#next-actions).

- [ ] Use skills
      Link provides skill files that are already written for Link Agent Wallet:

      - [Skill for purchasing](https://github.com/stripe/link-cli/blob/main/skills/create-payment-credential/SKILL.md)
      - [Skill for financial insights](https://github.com/stripe/link-cli/blob/main/skills/financial-insights/SKILL.md)

## Choose a credential type

Link Agent Wallet can generate a number of different types of payment credential, depending on what the seller supports.

| How the seller accepts payments | Which payment credential to use | How to complete payment |
| --- | --- | --- |
| A supported Stripe payment integration with Link enabled | A Link pay token | Enter the [Link Pay Token](https://docs.stripe.com/agentic-commerce/agents/link-agent-wallet/use-link-wallet-pay-online.md#pay-with-a-link-pay-token) into the checkout page. |
| An API accepting shared payment tokens directly or using Machine Payments Protocol (MPP) with `method="stripe"`. | A shared payment token | Enter the shared payment token directly to the API, or through [the machine-payment endpoint](https://docs.stripe.com/agentic-commerce/agents/link-agent-wallet/machine-payments.md). |
| A regular card form | A one-time use card number (the default) | Enter the [virtual card](https://docs.stripe.com/agentic-commerce/agents/link-agent-wallet/use-link-wallet-pay-online.md#retrieve-a-virtual-card) into the checkout form. |

## Create a spend request

Once you know which credential type to use, create a spend request. `--amount` and `--context` are always required. `--merchant-name` and `--merchant-url` are required for virtual cards, and are omitted for shared payment tokens and Link Pay Tokens. Link resolves the seller’s identity itself in those flows.

```bash
link-cli spend-request create \
  --amount 3500 \
  --context "Purchasing 'Working in Public' from press.stripe.com. The customer initiated this purchase through the shopping assistant." \
  --merchant-name "Stripe Press" \
  --merchant-url "https://press.stripe.com" \
  --line-item "name:Working in Public,unit_amount:3500,quantity:1" \
  --total "type:total,display_text:Total,amount:3500"
```

```json
{
  "id": "lsrq_abc123",
  "status": "pending_approval",
  "amount": 3500,
  "currency": "usd",
  "merchant_name": "Stripe Press",
  "merchant_url": "https://press.stripe.com",
  "credential_type": "card",
  "approval_url": "https://app.link.com/activity/approve/lsrq_abc123",
  "created_at": "2026-06-12T15:40:10Z",
  "_next": {
    "command": "spend-request retrieve lsrq_abc123 --interval 2 --max-attempts 300",
    "until": "status changes from pending_approval"
  }
}
```

Present `approval_url` to the customer.

Approval is requested by default. Pass `--no-request-approval` to create a request without asking for approval, then call `spend-request request-approval <id>` when you’re ready.

### Provide context for the customer 

`--context` must be at least 100 characters, and it’s what your customer reads when deciding whether to approve. Name the item, the seller, and why your agent is buying it.

### Parameters 

| Parameter | Required | Description |
| --- | --- | --- |
| `--amount` | Yes | Total to authorize, in the smallest currency unit. `3500` is 35.00 USD. |
| `--context` | Yes | What’s being bought and why, minimum 100 characters. Shown to the customer for approval. |
| `--merchant-name` | Conditional | Required for virtual cards. Omit for `shared_payment_token` and `link_pay_token`. |
| `--merchant-url` | Conditional | The product or seller page. Required for virtual cards. Omit for `shared_payment_token` and `link_pay_token`. |
| `--credential-type` | No | `card` (default), `shared_payment_token`, or `link_pay_token`. |
| `--network-id` | Conditional | Required for `shared_payment_token`. Get it from `link-cli mpp decode`. |
| `--merchant-account-id` | Conditional | The `acct_...` value read from `data-stripe-merchant-account`. Required with `--credential-type link_pay_token`. |
| `--currency` | No | Three-letter ISO code. Defaults to `usd`. |
| `--payment-method-id` | No | Which saved method to charge. Defaults to the customer’s default, or the first eligible one. |
| `--line-item` | No | An item on the approval screen. Repeatable. See [Line items and totals](https://docs.stripe.com/agentic-commerce/agents/link-agent-wallet/use-link-wallet-pay-online.md#line-items-and-totals). |
| `--total` | No | An amount line on the approval screen. Repeatable. |
| `--request-approval` | No | Request approval immediately. Defaults to true. |
| `--metadata` | No | Arbitrary `key:value` data. Repeatable. Maximum 50 keys, keys up to 40 characters, values up to 500. |
| `--idempotency-key` | No | Reuse only when retrying the same logical creation. Maximum 255 UTF-8 bytes. |
| `--expires-at` | No | Unix timestamp, in seconds, after which the request expires. |
| `--test` | No | Create a test mode request. Defaults to false. See [Test your integration](https://docs.stripe.com/agentic-commerce/agents/link-agent-wallet/use-link-wallet-pay-online.md#test-your-integration). |

### Line items and totals 

You can optionally also include details of what is being purchased.

`--line-item` and `--total` take repeatable `key:value` pairs and control what the customer sees at approval.

`--line-item` keys: `name` (required), `quantity`, `unit_amount`, `description`, `sku`, `url`, `image_url`, `product_url`.

`--total` keys: `type` (required), `display_text` (required), and `amount` (required). `type` is one of `subtotal`, `tax`, `total`, `items_base_amount`, `items_discount`, `discount`, `fulfillment`, `shipping`, `fee`, `gift_wrap`, `tip`, or `store_credit`.

```bash
link-cli spend-request create \
  --amount 13100 \
  --context "Purchasing trail running shoes from example.com. The customer selected these after comparing three options with the shopping assistant." \
  --merchant-name "Example Store" \
  --merchant-url "https://example.com/running-shoes" \
  --line-item "name:Running Shoes,unit_amount:12000,quantity:1,description:Trail runners" \
  --total "type:subtotal,display_text:Subtotal,amount:12000" \
  --total "type:shipping,display_text:Shipping,amount:1100" \
  --total "type:total,display_text:Total,amount:13100"
```

### Idempotency keys 

Pass `--idempotency-key` when creating a spend request to allow safely retrying for the same purchase intent. The server returns the original response for any repeated call that uses the same key.

Use a key that’s unique to the logical purchase attempt — for example, a UUID generated when the user initiates the purchase. Don’t reuse the same key for a different purchase. The key is opaque and non-sensitive: don’t include personal data or payment details in it. It must be between 1 and 255 UTF-8 bytes.

```bash
link-cli spend-request create \
  --amount 3500 \
  --context "Purchasing 'Working in Public' from press.stripe.com. The customer initiated this purchase through the shopping assistant." \
  --merchant-name "Stripe Press" \
  --merchant-url "https://press.stripe.com" \
  --idempotency-key "purchase-attempt-a1b2c3d4"
```

## Wait for approval

Poll until the customer approves or declines the request:

```bash
link-cli spend-request retrieve lsrq_abc123 --interval 2 --max-attempts 300
```

Polling only happens when `--interval` is greater than zero.

| Flag | Default | Behavior |
| --- | --- | --- |
| `--interval` | `0` | Seconds between polls. `0` means retrieve once and return. |
| `--max-attempts` | `0` | Maximum polls. `0` means unlimited. |
| `--timeout` | `600` | Seconds before giving up. The 600-second default is shorter than the approval window. |

Customers have 30 minutes from the approval request to respond, after which the status becomes `expired`.

## Retrieve a virtual card

By default, card details are not returned when retrieving a spend request. For hosted agents, write card details directly to a file rather than returning them to stdout, where they can persist in model transcripts and application logs:

```bash
link-cli spend-request retrieve lsrq_abc123 \
  --include card \
  --output-file /tmp/link-card.json \
  --format json
```

The file is created with `0600` permissions. The command fails instead of overwriting an existing file unless you pass `--force`. Standard output then carries only `brand`, `last4`, and expiration, plus a `card_output_file` path.

> #### Keep card details out of stdout
> 
> Passing `--include card` without `--output-file` writes the full PAN, CVC, and billing address to stdout, where it can be captured in model context, agent transcripts, and log aggregation. Use `--output-file` for hosted agents.

To retrieve card details directly to stdout (for example, in a local development context):

```bash
link-cli spend-request retrieve lsrq_abc123 --include card --format json
```

```json
{
  "id": "lsrq_abc123",
  "status": "approved",
  "card": {
    "brand": "visa",
    "number": "4242424242424242",
    "cvc": "100",
    "exp_month": 6,
    "exp_year": 2029,
    "billing_address": {
      "name": "Jenny Rosen",
      "line1": "123 Main St",
      "city": "San Francisco",
      "state": "CA",
      "postal_code": "94015",
      "country": "US"
    },
    "valid_until": "2026-06-13T03:40:10Z"
  }
}
```

Enter the card number, CVC, expiration, and billing address into the seller’s checkout form.

## Spend request statuses 

Spend requests transition through multiple states during their lifecycle. It is recommended to retrieve the spend request often to understand its current state. If your agent encounters issues or errors completing a payment, retrieve the latest spend request status and check for `requires_action`.

| Status | Meaning | What to do |
| --- | --- | --- |
| `created` | The request exists and approval hasn’t been requested yet. | Call `request-approval`, or keep polling if you passed `--request-approval`. |
| `pending_approval` | Waiting on the customer to approve. | Keep polling. |
| `requires_action` | Something needs attention before the request can proceed. | Branch on the [next action](https://docs.stripe.com/agentic-commerce/agents/link-agent-wallet/use-link-wallet-pay-online.md#next-actions). |
| `approved` | The customer approved. Credentials are available. | Retrieve the credential and pay. |
| `submitted` | The payment is in progress and will complete. | Nothing. Optionally [report the outcome](https://docs.stripe.com/agentic-commerce/agents/link-agent-wallet/use-link-wallet-pay-online.md#report-outcomes). |
| `succeeded` | The payment completed. | Nothing. Optionally [report the outcome](https://docs.stripe.com/agentic-commerce/agents/link-agent-wallet/use-link-wallet-pay-online.md#report-outcomes). |
| `failed` | The payment failed and the request is terminal. | Read `payment_status_details` for the reason before retrying. |
| `denied` | The customer declined. | Tell the customer. Don’t retry without creating a new request. |
| `expired` | The customer didn’t respond within the approval window, or the credential wasn’t consumed within the expiration window. | Create a new request if the customer still wants to proceed. |
| `canceled` | The request was canceled before completion. | Create a new request if the customer still wants to proceed. |

## Handle next actions 

A status of `requires_action` means the payment method or the account needs attention. It’s not a decline, and it can appear at creation or later from `retrieve`. Read `status_details.requires_action.next_action`:

| Field | Description |
| --- | --- |
| `type` | What the customer needs to do. |
| `resolution` | How your agent should proceed to resolve the issue. |
| `display_message` | Customer-facing text you can show as-is. |
| `action_url` | A Stripe-hosted URL to send the customer to, or `null`. |
| `expires_at` | When `action_url` stops working, as an ISO 8601 timestamp string. `null` when it doesn’t expire. |

Branch on `resolution`, not on `type`. The same underlying problem may appear as different types depending on context.

| Resolution | What it means | What to do |
| --- | --- | --- |
| `auto_resume` | After the customer completes the action, the spend request becomes available. | Send the customer to `action_url`, then keep polling the same request. Only `three_d_secure` uses this today. |
| `create_new_spend_request` | The request is unusable and a new one should be created once the issue is resolved. | Create a new request, adjusting whatever caused the problem. |
| `create_new_spend_request_after_completion` | You can’t use this request. Resolve the issue, then create a new request. | Send them to `action_url`, wait for them to finish, then create a new request. |

The action types and their resolutions:

| Action type | Resolution | What happened |
| --- | --- | --- |
| `three_d_secure` | `auto_resume` | The issuer wants a 3D Secure challenge. The request resumes on its own. |
| `three_d_secure_retry` | `create_new_spend_request` | The challenge wasn’t completed. |
| `select_payment_method` | `create_new_spend_request` | The payment method was declined. Ask the customer to pick another. |
| `update_payment_method` | `create_new_spend_request` | The payment method needs updating. |
| `re_authorize` | `create_new_spend_request` | The charge exceeded the approved amount. Create a request for the correct amount. |
| `add_payment_method` | `create_new_spend_request_after_completion` | No eligible payment method is on file. Ask the customer to add one. |
| `ssn_verification` | `create_new_spend_request_after_completion` | The customer needs to verify their identity. |
| `identity_verification` | `create_new_spend_request_after_completion` | The customer needs additional identity verification. |
| `contact_support` | `create_new_spend_request_after_completion` | Send the customer to Link support. |

Only `auto_resume` keeps the existing request alive. For all other types, the old request expires on its own. Don’t cancel it first, and don’t poll it.

> You can also check for known blockers before you start, which saves your customer a failed approval:
> 
> ```bash
> link-cli user-info retrieve --format json
> ```
> 
> The response includes `agent_wallet_spend_limits`, with `per_transaction`, `daily`, and `thirty_day` entries carrying `limit`, `used`, and `remaining`. Finite values are in cents; `null` means unlimited. It also includes `agent_wallet_verification_requirement`, whose `status` and nullable `action_url` tell you whether the customer must verify something first.

## Read a failed payment 

Use `payment_status_details` to inspect the latest recorded payment and `payment_history` for all historical payments. For failures, check the payment’s `code` and `decline_code`.

| Field | Description |
| --- | --- |
| `outcome` | `success` or `failure`. |
| `code` | The failure code, when there is one. |
| `decline_code` | The issuer’s decline code, when the issuer declined. |
| `amount` and `currency` | The payment’s amount and currency. |
| `refund_details` | Amount, currency, state, and creation time, when a refund exists. |

## Increasing the amount on an approved request 

Agents may not always know the final price at approval time. A checkout might need card details before it reveals shipping or tax. To increase the amount on an already approved request, update the request, then ask for approval again:

```bash
link-cli spend-request update lsrq_abc123 --amount 7500

link-cli spend-request request-approval lsrq_abc123

link-cli spend-request retrieve lsrq_abc123 --interval 2 --max-attempts 300
```

Pass the new total, not the difference. Raising the amount requires the customer to approve again. On approval, Link increments the authorization so the card covers the higher amount.

Before requesting an increase, check that the customer has already approved the request and that you haven’t successfully used the card yet. A virtual card can’t be used twice.

`update` also accepts `--merchant-url`, `--currency`, `--payment-method-id`, `--line-item`, and `--total`, so you can correct what the customer sees along with the amount.

Increases aren’t guaranteed. A safe default is to try, and fall back to canceling and recreating on error. The spend request stays usable at its original amount in the case it could not be increased.

## Pay with a Link Pay Token 

Supported Stripe payments integrations expose a field that accepts a Link Pay Token. This token allows your agent to complete the entire payment step, including billing and shipping addresses, without needing to manually fill in all the form fields.

Select `link_pay_token` as the credential type when creating a Link Pay Token spend request.

1. Open the checkout page and find the steering block. It’s visually hidden and often inside a Stripe iframe, so search the top document and every Stripe frame for `.AiAgentPaymentSteering`. Run the remaining steps in whichever frame contains it.

2. Confirm the block contains an `input[name="link_pay_token"]` field, and read the merchant account ID from the same frame:

   ```javascript
   const merchantAccountId = document
     .querySelector('.AiAgentPaymentSteering [data-stripe-merchant-account]')
     ?.getAttribute('data-stripe-merchant-account');
   ```

   If `input[name="link_pay_token"]` is missing, or `merchantAccountId` is empty, stop and create an ordinary virtual card request instead.

3. Create the spend request. Include `--credential-type link_pay_token` and `--merchant-account-id`, and omit `--merchant-name` or `--merchant-url`:

   ```bash
   link-cli spend-request create \
     --credential-type link_pay_token \
     --merchant-account-id acct_YOUR_MERCHANT_ACCOUNT \
     --amount 3500 \
     --context "Purchasing a subscription from the checkout page the agent inspected. The customer initiated this purchase through the shopping assistant." \
     --total "type:total,display_text:Total,amount:3500"
   ```

4. After approval, retrieve the token. Each token is valid for up to 30 minutes, or until the spend request expires:

   ```bash
   link-cli spend-request retrieve lsrq_abc123 --include link_pay_token --format json
   ```

5. Inject it with the native value setter instead of typing it character by character:

   ```javascript
   const input = document.querySelector('input[name="link_pay_token"]');
   Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')
     .set.call(input, token);
   input.dispatchEvent(new Event('input', {bubbles: true}));
   ```

6. Wait for the card form to be replaced by a single saved card showing the customer’s email, then submit the payment.

## Using saved shipping addresses 

When a checkout needs a shipping address, your agent can use the ones already saved in their Link Agent Wallet:

```bash
link-cli shipping-address list --format json
```

Each record has an `id`, an `is_default` flag, a nullable `nickname`, and a nullable `address`. Use the default unless the customer picks another.

Address fields use `line_1`, `line_2`, `locality`, `dependent_locality`, `administrative_area`, `postal_code`, `sorting_code`, and `country_code`, and any of them can be `null`.

## Report outcomes 

Reporting what happened after a purchase attempt is optional, and it’s how Stripe finds the checkout flows that break agents and need improving.

```bash
link-cli report \
  --domain shop.example.com \
  --outcome success \
  --spend-request-id lsrq_abc123
```

`--outcome` is `success` when the order was confirmed, `blocked` when something stopped your agent, or `abandoned` when your agent chose to stop.

Add `--tag` to classify what happened. It’s repeatable, and the values are `stripe_checkout`, `captcha`, `anti_bot_script`, `cdn_block`, `waf_block`, `dns_block`, `rate_limited`, `login_required`, `3ds_challenge`, `page_inaccessible`, `timeout`, `site_error`, `payment_declined`, and `other`. Prefer the most specific tag, and describe anything else in `--freeform-context`.

```bash
link-cli report \
  --domain shop.example.com \
  --outcome blocked \
  --spend-request-id lsrq_abc123 \
  --tag captcha \
  --step "checkout page" \
  --freeform-context "Turnstile challenge appeared after clicking Pay now"
```

`--step` records where your agent was when the outcome happened, up to 500 characters. `--attempt-trace` records the whole path, as one numbered line per step giving the URL path, what your agent acted on, the action, and the result. Send a trace on failures too: the dead ends are what stop the next agent from repeating them.

Leave the buyer’s personal data out of a trace. Write `[email]` and `[address]` in its place. Traces longer than 8,000 characters are truncated by the server instead of rejected, so send the full narrative instead of skipping the report.

## Manage spend requests 

```bash
link-cli spend-request list

link-cli spend-request retrieve lsrq_abc123

link-cli spend-request cancel lsrq_abc123
```

Use `list` to return active requests, and add `--include-history` for expired and terminal ones. `retrieve` shows the transaction’s details, including refunds and payment events. Use `cancel` when your agent needs to cancel an unused request.

## Test your integration 

Pass `--test` to create a test mode spend request. It returns test credentials, such as card number `4000009990001984`, and never charges the underlying payment method:

```bash
link-cli spend-request create \
  --test \
  --amount 3500 \
  --context "Purchasing 'Working in Public' from press.stripe.com. The customer initiated this purchase through the shopping assistant." \
  --merchant-name "Stripe Press" \
  --merchant-url "https://press.stripe.com"
```

## See also

- [Authorize your agent](https://docs.stripe.com/agentic-commerce/agents/link-agent-wallet/oauth.md)
- [Pay machine-payment businesses](https://docs.stripe.com/agentic-commerce/agents/link-agent-wallet/machine-payments.md)
- [Add financial insights to your agent](https://docs.stripe.com/financial-connections/agents/financial-insights.md)
