# Retrieve payment credentials with spend requests

Let your agent retrieve one-time-use payment credentials that customers approve in Link.

Agent payments are available to US consumers. The sellers your customers buy from can be outside the US.

A Link spend request lets your agent retrieve a one-time-use payment credential to buy anywhere online. Your agent creates a spend request, the customer approves it, and Link returns the credential. Your agent then completes the purchase with that credential.

Spend requests support several payment options, including one-time-use virtual cards and [shared payment tokens](https://docs.stripe.com/agentic-commerce/concepts/shared-payment-tokens.md). A one-time-use virtual card works at any seller that accepts cards online. A shared payment token works only at sellers that accept shared payment tokens.

Link CLI gives you one integration across payment rails. Your agent requests a credential without integrating each rail directly.

## Before you begin

Before you begin, make sure that:

- You’ve [set up OAuth](https://docs.stripe.com/agentic-commerce/link-cli/oauth.md), including the `payment_methods.agentic` scope.
- You’ve installed Link CLI and keep it up to date: `npm install -g @stripe/link-cli`. Update it regularly to get the latest commands and skills.

## How spend requests work 

1. Your agent creates a spend request with the purchase amount, merchant, context, and payment credential type.
2. Link returns an `approval_url` to present to the customer.
3. The customer approves the request on the Link website or in the Link mobile app. Customers don’t need to install the mobile app to approve.
4. Your agent polls the spend request until it sees that the request is approved.
5. Link returns a one-time-use payment credential your agent uses to complete the purchase.

## Create a spend request

To create a spend request, use the `spend-request create` command:

```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",
  "merchant_name": "Stripe Press",
  "merchant_url": "https://press.stripe.com",
  "context": "Purchasing 'Working in Public' from press.stripe.com. The customer initiated this purchase through the shopping assistant.",
  "amount": 3500,
  "currency": "usd",
  "status": "pending_approval",
  "approval_url": "https://app.link.com/activity/approve/lsrq_abc123",
  "credential_type": "card",
  "_next": {
    "command": "spend-request retrieve lsrq_abc123 --interval 2 --max-attempts 300",
    "until": "status changes from pending_approval"
  }
}
```

Present the `approval_url` to the customer and ask them to approve the purchase.

By default, `spend-request create` returns a `card` credential—a one-time-use virtual card. Set `--credential-type` to choose the type that matches how your agent pays:

| Credential type | When to use it |
| --- | --- |
| Virtual card (`card`, default) | Buying on the open web, at any merchant that accepts cards. |
| Shared payment token (`shared_payment_token`) | Paying sellers that accept shared payment tokens, including [machine-payment merchants](https://docs.stripe.com/agentic-commerce/link-cli/machine-payments.md). |

### Parameters 

The `spend-request create` command accepts the following parameters:

| Parameter | Required | Description |
| --- | --- | --- |
| `--amount` | Yes | Purchase amount in the smallest currency unit (for example, 3500 for 35.00 USD). |
| `--context` | Yes | Description of what’s being purchased and why. Must be at least 100 characters. This appears to the customer in the approval prompt. |
| `--merchant-name` | Conditional | The merchant where the purchase occurs. Required for card requests. Omit for `shared_payment_token` requests. |
| `--merchant-url` | Conditional | The URL of the product or merchant page. Required for card requests. Omit for `shared_payment_token` requests. |
| `--network-id` | Conditional | Required for `shared_payment_token` requests. Retrieve it from the seller’s `profile_id` when you use [UCP](https://docs.stripe.com/agentic-commerce/link-cli/commerce-agents-ucp.md), or with `mpp decode` when you [pay machine-payment merchants](https://docs.stripe.com/agentic-commerce/link-cli/machine-payments.md). |
| `--payment-method-id` | No | The payment method to charge. Defaults to the customer’s default payment method, or the first eligible one when no default is set. |
| `--credential-type` | No | Type of payment credential to return: `card` (default) or `shared_payment_token`. |
| `--currency` | No | Three-letter ISO currency code. Defaults to `usd`. |
| `--line-item` | No | A line item to display on the approval screen. Repeatable. |
| `--total` | No | A total to display on the approval screen. Repeatable. |
| `--metadata` | No | Arbitrary string data in `key:value` format. Repeatable. Maximum 50 keys, keys up to 40 characters, values up to 500 characters. |
| `--test` | No | Creates a test mode spend request that returns test credentials without a real charge. Defaults to `false`. |

### Line items and totals 

The `--line-item` and `--total` parameters take repeatable `key:value` pairs. They control what the customer sees on the approval screen, so include them when the purchase has more than one component.

The keys for the `--line-item` parameter, where `name` is required:

- `name`
- `quantity`
- `unit_amount`
- `description`
- `sku`
- `url`
- `image_url`
- `product_url`

The keys for the `--total` parameter, where all three are required:

- `type`, one of `subtotal`, `tax`, `total`, `items_base_amount`, `items_discount`, `discount`, `fulfillment`, `shipping`, `fee`, `gift_wrap`, `tip`, or `store_credit`
- `display_text`
- `amount`

For example, to create a spend request for a purchase with multiple items, run:

```bash
link-cli spend-request create \
  --amount 12000 \
  --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:total,display_text:Total,amount:12000"
```

## Poll for approval

After presenting the approval URL, poll the spend request until it reaches a terminal status:

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

The `--interval` flag sets the polling interval in seconds, and `--max-attempts` sets how many polls to make before giving up. Polling exits successfully only after the request reaches a terminal status: `approved`, `denied`, `expired`, or `canceled`.

The customer has 10 minutes from the approval request to respond, after which the status becomes `expired`. Size `--interval` and `--max-attempts` to cover that window. See [Limits](https://docs.stripe.com/agentic-commerce/link-cli/use-link-wallet-pay-online.md#limits).

### Handle interruptions while polling 

Two outcomes stop polling before a terminal status:

- **A status of `requires_action`**: The customer needs to do something before the request can proceed. Present `next_action.display_message` and `next_action.action_url` to the customer, then resume polling. See [Next actions](https://docs.stripe.com/agentic-commerce/link-cli/use-link-wallet-pay-online.md#next-actions) for how to branch on each action type.
- **An exhausted timeout**: If `--timeout` elapses or `--max-attempts` runs out while the request is still non-terminal, the command exits non-zero with `code: "POLLING_TIMEOUT"`.

> #### Don't treat a polling timeout as a completed purchase
> 
> A `POLLING_TIMEOUT` exit means the request is still pending, not that it failed or succeeded. Retrieve the spend request again to determine its actual status before you retry or tell the customer anything.

## Retrieve card payment credentials

Retrieving a spend request excludes card details by default, to keep the cardholder’s PII out of any response shown to the customer. Pass `--include card` to return them:

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

To keep the full card out of your agent’s transcript and logs, write it to a file with `--output-file`. Standard output then carries only redacted fields:

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

Card numbers printed to standard output can persist in agent transcripts, model context, and log aggregation. Use `--output-file` in any hosted agent. Link CLI creates the file with `0600` permissions, and fails rather than overwriting an existing file unless you pass `--force`.

When you set `--output-file`, the JSON output replaces the `card` object with redacted fields and adds a `card_output_file` path pointing at the file. The approved spend request contains the credential:

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

The card is one-time-use. By default, it expires 12 hours after you create the spend request, at the time shown in `valid_until`.

## Complete the purchase

Use the card details from the approved spend request to complete checkout on the merchant’s site. Your agent can fill in the card number, CVC, expiration date, and billing address at checkout.

## Limits 

These limits apply per agent integration. To raise them for your integration, contact your [Stripe sales](https://stripe.com/contact/sales) representative.

| Limit | Value |
| --- | --- |
| Per request | 500 USD |
| Daily | 500 USD |
| Monthly, over 30 days | 20,000 USD |
| Concurrent active requests | 30, counting `created` and `approved` |
| Concurrent approved requests | 10 |
| Creation rate | 50 per hour, and 200 over 60 days |
| Approval window | 10 minutes from the approval request |
| Credential validity | 12 hours from spend request creation |

## Spend request statuses 

A spend request moves through one of nine statuses. Handle each one explicitly, because polling returns whatever the current status is.

| Status | Meaning | Action |
| --- | --- | --- |
| `created` | The request exists and approval will be requested. | Continue polling until the status changes to `pending_approval`. |
| `pending_approval` | Waiting for the customer to approve or deny. | Continue polling. |
| `requires_action` | The customer needs to complete an action before the request can proceed. | Present `next_action.display_message` and `action_url` to the customer, then follow [Next actions](https://docs.stripe.com/agentic-commerce/link-cli/use-link-wallet-pay-online.md#next-actions). |
| `approved` | The customer approved the request. Payment credentials are available. | Complete the purchase. |
| `denied` | The customer denied the request. | Tell the customer, and don’t retry without creating a new request. |
| `expired` | The customer didn’t respond within the approval window. | Create a new spend request if the customer still wants to proceed. |
| `canceled` | The request was canceled before completion. | Create a new spend request if the customer still wants to proceed. |
| `succeeded` | The payment completed. | No further action. |
| `failed` | The payment failed and the request is terminal. | Read `status_details.failed.code` for the failure reason before retrying. |

## Next actions 

When a spend request has a status of `requires_action`, read `status_details.requires_action.next_action`. It contains:

| Field | Description |
| --- | --- |
| `type` | The kind of action the customer needs to complete. |
| `resolution` | How your agent proceeds after the customer acts. |
| `display_message` | A customer-facing string you can present directly to the customer. |
| `action_url` | A Stripe-hosted URL to send the customer to, when one applies. |
| `expires_at` | When `action_url` expires, as an ISO 8601 string. `null` when there’s no expiry. |

Branch on `type` and `resolution` rather than on `failure_code` alone, because the same `failure_code` maps to different action types depending on context.

| Action type | Resolution | What to do |
| --- | --- | --- |
| `ssn_verification` | `create_new_spend_request_after_completion` | Send the customer to `action_url` to verify their identity, then create a new spend request. |
| `identity_verification` | `create_new_spend_request_after_completion` | Send the customer to `action_url` for additional identity verification, then create a new spend request. |
| `contact_support` | `create_new_spend_request_after_completion` | Identity verification attempts are exhausted. Send the customer to Link support. |
| `select_payment_method` | `create_new_spend_request` | The payment method was declined. Ask the customer to choose another, then create a new spend request. |
| `add_payment_method` | `create_new_spend_request_after_completion` | No eligible payment method is on file. Send the customer to `action_url` to add one, then create a new spend request. |
| `update_payment_method` | `create_new_spend_request` | The card needs attention, such as an expiry, address, or security code problem. Send the customer to `action_url`, then create a new spend request. |
| `re_authorize` | `create_new_spend_request` | The charge exceeded the approved amount. Create a new spend request for the correct amount. |
| `three_d_secure` | `auto_resume` | Send the customer to `action_url` to complete the challenge, then keep polling. The request resumes on its own. |
| `three_d_secure_retry` | `create_new_spend_request` | The challenge wasn’t completed. Create a new spend request. |

Only `auto_resume` keeps the existing spend request alive. Every other resolution means the request is terminal, so create a new one after the customer acts.

## Raise the amount on an approved request 

Agents don’t always know the final price when they request approval. A checkout might require card details before it shows the final total, including tax or shipping. Rather than requesting a buffer amount upfront, you can raise the amount on a spend request the customer already approved.

Raising the amount requires the customer to approve again. On approval, Link triggers an incremental authorization so the card can be charged for the higher amount.

Before you call `update`, check that:

- The request supports incremental authorization. Read `incremental_auth_enabled` on the `spend-request retrieve` response.
- The customer already approved the request.
- You haven’t successfully used the card yet. A virtual card can’t be used more than once.

Pass the new total, not the difference:

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

Then ask the customer to approve again and poll for their decision:

```bash
link-cli spend-request request-approval lsrq_abc123

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

Incremental authorizations aren’t guaranteed. A safe default is to try the increase and fall back to canceling and recreating the request on any error. In every failure case, the spend request stays usable at its original amount—a failed increase never invalidates credentials you already hold.

| What happened | How it surfaces | What to do |
| --- | --- | --- |
| The increase succeeded | `status: approved` with the higher amount | Use the card you already retrieved. |
| The issuer declined the increase | A payment-declined error | Proceed at the original amount, or cancel and recreate. |
| The increase exceeds the customer’s limits | A limits error with a specific code | Branch on the code. The request is unchanged and still usable. |
| The card was already used | A conflict error | Poll `retrieve`. Create a new request if you still need to spend more. |
| The card doesn’t support incremental authorization | A `not_supported` error | Cancel and recreate. |

Amount is the only field you can change this way. The card details stay the same throughout.

## Manage a spend request 

Retrieve a spend request to see the details of the transaction, including refunds and payment success events:

```bash
link-cli spend-request retrieve lsrq_abc123
```

List all spend requests for the authorized customer:

```bash
link-cli spend-request list
```

Cancel a request from the `created`, `pending_approval`, or `approved` state:

```bash
link-cli spend-request cancel lsrq_abc123
```

## Test your integration 

Pass `--test` to create a test mode spend request. Link CLI returns test credentials, such as card number `4000009990001984`, and doesn’t charge 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"
```

To walk through both credential types interactively, run `link-cli demo`, which always uses test mode.

## See also

- [Search products and complete payments with UCP](https://docs.stripe.com/agentic-commerce/link-cli/commerce-agents-ucp.md)
- [Pay machine-payment merchants](https://docs.stripe.com/agentic-commerce/link-cli/machine-payments.md)
