Skip to content
RaydricPublic
forked from kvishalv/link-cli

About

Let your agents spend on your behalf. Your payment credentials are never exposed. You approve every purchase.

Resources

Code of conduct

Contributing

Stars

0 stars

Watchers

0 watching

Forks

 
 

Latest commit

 

History

365 Commits

Folders and files

Repository files navigation

Link CLI

Link CLI lets agents get secure payment credentials from a Link wallet for one-time purchases and recurring payments — without storing your real card details.

The CLI can produce one of three credential types:

  • A virtual card (PAN) for use with a standard web checkout form. The issued card works anywhere, and is not restricted to Link-enabled sellers or sellers that use Stripe.
  • A Link Pay Token (LPT) for use on supported Stripe payment surfaces. Stripe checkout pages use WebMCP to allow agents to complete the checkout.
  • A Shared Payment Token (SPT) for use when the seller accepts programmatic payments through Machine Payment Protocols (MPP)

For now, this is only available to US and Canadian Link accounts.

Documentation:

Tip

If you're looking to integrate the Link CLI into your product or agent for consumers to pay with, be sure to look at Integrating into your agent.

Installation

npm i -g @stripe/link-cli

Agent and plugin integrations, including the MCP server, run the installed link-cli binary. They intentionally never download or resolve packages at startup, so install the CLI first.

Use with agents

Install the skills:

npx skills add stripe/link-cli

By default when called from an agent (non-TTY), all commands use toon output — a compact, LLM-friendly text format. All commands accept --format [format] for structured output. Other formats: json, yaml, md, jsonl.

List available commands:

link-cli --llms-full

Get a command's full schema with --schema. Example:

link-cli spend-request create --schema

MCP Server

Link CLI can run as a local MCP server. Add the following to your MCP client config (.mcp.json, etc.)

{
  "mcpServers": {
    "link": {
      "command": "link-cli",
      "args": ["--mcp"]
    }
  }
}

Alternatively, use serve to expose link-cli as an MCP endpoint over HTTP.

link-cli serve            # binds 127.0.0.1:54321 (loopback only)
link-cli serve --port 8080
link-cli serve --host 0.0.0.0   # expose beyond localhost (see warning below)
link-cli serve --allow-origin http://localhost:3000   # trust a specific browser app

The server handles POST /mcp and GET skill discovery at /.well-known/skills/index.json and /.well-known/skills/{skill-name}/SKILL.md, with OPTIONS preflight support. Other paths return 404, unsupported methods return 405, and malformed or ambiguous request paths return 400. It binds to 127.0.0.1 by default so only the local host can reach it. Anyone who can reach the port can use this CLI's authenticated Link session, so only override --host on a trusted, isolated network — doing so prints a warning.

A browser attaches an Origin header; MCP clients and curl do not. Because any JavaScript on any http://localhost:* page the user visits would satisfy a bare "loopback origin" check without being the authenticated CLI owner, serve rejects every browser Origin by default (403, no CORS header) and validates the Host header against loopback to block DNS rebinding. Requests without an Origin (ordinary MCP clients) are unaffected. To integrate a browser app you control, allow its exact origin with --allow-origin (repeatable); only that origin is reflected in Access-Control-Allow-Origin.

Quickstart

Run a guided onboarding and demo flow:

link-cli onboard

Login

The link-cli requires a Link account. You can log in to your existing one or create one online.

link-cli auth login

You receive a verification URL and a short phrase. Visit the URL, log in to your Link account, and enter the phrase to approve the connection.

By default, login requests the userinfo:read and payment_methods.agentic scopes plus the four financial-insights source actions. To leave out financial data access, use --no-financial-insights; to leave out payment access, use --no-agentic-payments.

Retrieve user info

link-cli user-info retrieve --format json

In addition to identity fields, the response can include the user's address, spend limits, and verification requirements:

{
  "email": "jane@example.com",
  "name": "Jane Doe",
  "address": {
    "line1": "510 Townsend St",
    "line2": null,
    "city": "San Francisco",
    "state": "CA",
    "postal_code": "94103",
    "country": "US"
  },
  "agent_wallet_spend_limits": {
    "per_transaction": { "limit": 50000 },
    "daily": { "limit": 500000, "used": 120000, "remaining": 380000 },
    "thirty_day": { "limit": null, "used": 600000, "remaining": null }
  },
  "agent_wallet_verification_requirement": {
    "status": "identity_verification",
    "action_url": "https://app.link.com/finish_setup?verify=identity_verification&intended_email=jane%40example.com&fromEmail=jane%40example.com"
  }
}

The address field is omitted when its enrichment is unavailable and is null when the user has no Person record. Finite spend-limit values are cents because this response does not include a currency. A null limit or remaining amount means unlimited. agent_wallet_verification_requirement.status can be not_required, ssn_verification, credit_file_match_ca, identity_verification, doc_upload_ca, pending, contact_support, or complete. Its action_url is null when no user action is available.

Retrieve approval policy

Retrieve the rules that grant the current app authority to create spend requests without manual approval:

link-cli approval-policy retrieve --format json

Each rule includes an action, a per-purchase limit, and optionally an ordered list of allowed payment method IDs. The API returns an error when no approval policy has been configured.

List payment methods

link-cli payment-methods list

Returns the cards and bank accounts saved to your Link account. Use the id field as payment_method_id in the next step. If you have no payment methods, add new ones in Link.

The list can also include a Link balance payment method with its available balance when that amount is available.

Retrieve one payment method by ID:

link-cli payment-methods retrieve <payment-method-id>

The response contains the same redacted fields as the matching list item, including capability eligibility when available.

Set or change a payment-method nickname:

link-cli payment-methods update <payment-method-id> --nickname "Work card"

Clear a nickname by passing an explicit empty string:

link-cli payment-methods update <payment-method-id> --nickname ""

Set a payment method as the wallet default, or clear its default status:

link-cli payment-methods update <payment-method-id> --is-default true
link-cli payment-methods update <payment-method-id> --is-default false

You can pass --nickname and --is-default together. Supply at least one of them; omitted fields stay unchanged. Setting a new default replaces the previous wallet default. Link trims surrounding nickname whitespace and returns the updated, redacted payment method.

List shipping addresses

link-cli shipping-address list

Returns the shipping addresses saved to your Link account. The response preserves nullable nickname, address, and address fields exactly as returned by the API.

Create a spend request

Create a spend request with merchant details, line items, and amounts. If --payment-method-id is omitted, your default payment method will be used, or the first eligible one if no default is set:

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

The --request-approval flag triggers a push notification to the user for approval. Interactive mode polls until the request leaves the approval waiting states. Agent mode returns a spend-request retrieve command to wait for a status change, including a transition to submitted.

Easily approve requests with the Link app.

Execute payment

The approved spend request includes a card object with number, cvc, exp_month, exp_year, billing_address, and valid_until. Enter these into the merchant's checkout form.

link-cli spend-request retrieve lsrq_001

By default, retrieving a spend request doesn't include card details. Pass --include card to see unmasked card details.

To avoid leaking card credentials into agent transcripts or logs, use --output-file to write the full card to a secure local file while stdout shows only redacted data (brand, last4, expiry):

link-cli spend-request retrieve lsrq_001 --include card --output-file /tmp/link-card.json --format json

The file is created with 0600 permissions. If the file already exists, the command fails unless --force is passed. When --output-file is set, the JSON output replaces the card object with redacted fields and adds a card_output_file path.

To inspect only the existing user action in one snapshot:

link-cli spend-request retrieve lsrq_001 --next-action --format json

This returns one JSON result item, [{"next_action": <server action or null>}], including actions on requires_action and failed requests. It does not poll or return credentials. --include, --output-file, --force, and nonzero --interval/--max-attempts are incompatible; --timeout is ignored. Human TTY invocations require an explicit format. Use ordinary retrieve to inspect status or poll.

For agent polling, pass --interval and optionally --max-attempts:

link-cli spend-request retrieve lsrq_001 --interval 2 --max-attempts 300

Where to use Link Agent Wallet

Link is already integrated with the following agents:

Financial Insights

Link CLI can also read a consumer's financial data -- transactions, balances, and connected account details. Agents can use these features to answer personal finance questions and track trends. Financial Insights are powered by Financial Connections, covering 12,000+ US financial institutions.

Financial Insights also provides pre-computed and user-provided insights to power better shopping experiences through the insights commands.

Authentication

auth login requests all four financial-insights source actions by default: read_link_transactions, read_external_transactions, read_balances, and read_source_details. The user decides whether to approve the connection; requesting access does not retrieve financial data.

link-cli auth login --client-name "My Agent"

Use --no-financial-insights for a payment-only login, or --no-agentic-payments for a login without the default payment scope. To request a narrower set of financial-data actions, pass the desired --source-actions flags or --authorization-detail entries; either replaces the four-action default. Don't combine either option with an explicit --financial-insights or --no-financial-insights flag. An explicit --scope replaces the default scopes. If already authenticated, use auth upgrade --source-actions <action> to add only the access needed without dropping existing access.

Discovering available insight types only requires an authenticated session. Retrieving an insight can require additional source actions, which the discovery response identifies.

Discover and list user-supplied and pre-aggregated insights

link-cli insights list-available-types --format json
link-cli insights list --insight <insight_id> --format json

The first command lists available insight IDs, descriptions, and any access needed to retrieve them. The second returns the selected insight's status and data. Omit --insight to list all insights, or repeat it for several IDs to request only a few. Both commands support --limit and --starting-after to retrieve paginated results. If the provided access token is insufficient to view an insight, a no_data result will include authorization_remediation for missing permissions. A pending result means the insight is still being computed.

List sources

link-cli sources list

Returns connected financial accounts (bank accounts, credit cards, etc.) with metadata, capabilities, and connection status. Use the id field as --source in other commands.

List transactions

link-cli transactions list

Supports server-side filtering:

link-cli transactions list --start-date 2026-01-01 --end-date 2026-01-31
link-cli transactions list --category groceries
link-cli transactions list --origin external_connection
link-cli transactions list --source <source_id>
Flag Description
--start-date Only transactions on or after this date (YYYY-MM-DD)
--end-date Only transactions on or before this date (YYYY-MM-DD)
--category Filter by transaction category
--origin link (Link-native) or external_connection (from linked bank/card)
--source Filter by source ID (repeatable for multiple accounts)
--limit Max results per page (1–100)

Amounts are integers in the currency's smallest unit. Negative = money leaving the account, positive = money entering. Transactions may be Link-native (processed directly through Link), or sourced through an external connection (e.g. imported from transactions that would appear on a bank statement).

Agent integration

The financial-insights skill teaches agents which command to run for each question type, how to handle pagination, interpret amounts, and summarize results. See skills/financial-insights/SKILL.md for the full agent guide.

List balances

link-cli balances list
link-cli balances list --source <source_id>

Returns current balances for connected accounts, including cash.available (bank/savings) or credit.used (credit cards).

Advanced

Authentication

link-cli auth login --client-name "Claude Code"   # identify the connecting agent
link-cli auth login --client-name "Claude Code" --interval 5 --timeout 300  # login + poll in one call
link-cli auth upgrade --scope "userinfo:read"        # widen access to a superset
link-cli auth status                               # check auth status
link-cli auth logout                               # disconnect

When you provide --client-name, the Link app displays it when you approve the connection — for example, Claude Code on my-macbook instead of link-cli on my-macbook.

With --interval, the login command yields the verification code immediately and then polls inline until authenticated or time out — no separate auth status call needed. This is recommended for agents that cannot relay the code while a separate polling command blocks their I/O channel.

auth upgrade accepts the same explicit access flags as auth login, but does not add the login-only financial-insights default. Use it to widen an existing session. auth login stops with an "already logged in" message when a valid session exists; auth upgrade merges the requested access with the current scope and authorization_details and starts approval for the superset.

If there is no valid session, auth upgrade warns and continues with only the requested access. An existing session remains valid during approval and is replaced only after the new grant is approved, so abandoning the approval leaves the old session working.

auth status reports the scope and authorization_details the current session was granted (echoed by the token endpoint at login/refresh and stored in the credential file), and includes an update field when a newer version is available:

{
  "authenticated": true,
  "scope": "userinfo:read payment_methods.agentic",
  "authorization_details": [{ "type": "source", "actions": ["read"] }],
  "update": {
    "current_version": "0.1.2",
    "latest_version": "0.2.0",
    "update_command": "npm install -g @stripe/link-cli"
  }
}

scope and authorization_details are only present when the token endpoint returned them.

Set NO_UPDATE_NOTIFIER=1 to suppress update checks (for example, in CI).

All commands accept --auth <path> to store auth credentials in a specific file instead of the default location. auth login writes to this file; all other commands read from it. Useful for running multiple sessions with separate identities.

Identity (beta)

Services can verify these credentials with Agent Identity, a separate SDK in this repository. The event-registration example shows site access using an attestation and registration using a verified email.

Identity commands require Link Agent Wallet v0.27.0 or later for the flow documented here. Check link-cli --version; install or update with npm install -g @stripe/link-cli@latest. They are available in --help and --llms without a preview flag, but remain excluded from MCP. See wallet setup and the identity skill.

Both identity request commands save their artifacts to disk and return only the file path and metadata. This applies to every output format, including JSON, piped output, and --full-output. Request output never includes credentials, tokens, or claim values.

Privacy-preserving attestations that let a service verify Link issued the token, without disclosing personal details or identifying the agent:

link-cli identity attestations request --count 10

Each request adds tokens to the CLI-managed pool at ~/.link-cli/identity/attestations/pool.json. Pop one token when you need to answer an attestation challenge:

link-cli identity attestations pop --format json

pop removes one token before returning its token, ready-to-use authorization header, issuer, and issuer-key ID. Pass authorization as the Authorization header in your browser automation or HTTP client. An empty pool returns ATTESTATION_POOL_EMPTY; refill it with request --count 10.

For agent-managed tokens, export a batch to a new file outside the CLI storage directory:

link-cli identity attestations request --count 10 --output-file ./aats.json

Exported tokens never enter the CLI pool. The agent owns their consumption and cleanup. Existing exports remain separate and are never automatically imported. Wallet credentials keep their existing storage and behavior.

Pool updates are serialized and saved atomically. A crash after removal can lose a token; pop never returns it to the pool. If a crash leaves pool.json.lock, ensure no attestation commands are running before removing that lock directory.

User info that has been signed, proving it comes from Link:

link-cli identity credentials request

identity credentials request saves a signed credential to ~/.link-cli/identity/credentials/current.json, bound to the CLI-managed holder key at ~/.link-cli/identity/holder-key.jwk. Structured output includes output_file, issuer, expiry, holder-key path/thumbprint, and claim names. Use identity credentials present to sign selected claims, capture its JSON output programmatically, and send the presentation through browser automation or an HTTP client without printing it into the agent transcript.

Presentations disclose selected claims to a verifier using the saved credential and holder key:

link-cli identity credentials present \
  --aud https://directory.example \
  --nonce '<nonce-from-challenge>' \
  --claim email \
  --format json
{"presentation":"<issuer-jwt>~<email-disclosure>~<key-binding-jwt>"}

Use the verifier challenge's exact audience and nonce. Repeat --claim to disclose additional claims, such as --claim email --claim email_verified; at least one claim is required. The command reads ~/.link-cli/identity/credentials/current.json, signs with its saved holder key, and returns only the presentation. It works without login or network access and does not modify the saved credential or key. Missing claims, expired credentials, mismatched keys, and unsupported disclosure formats fail instead of producing a presentation. It supports Link's flat SHA-256 disclosures; credentials with plaintext user claims or nested selective disclosures are rejected.

Send the returned presentation as the Identity-Presentation HTTP header.

The verifier still validates the issuer signature, holder signature, audience, nonce, expiry, and required claims. Presentations include a fresh signing time and should be sent promptly. Follow the service's retry instructions. Completed interactions may return a saved result; a new operation or expired interaction may require a fresh challenge. The Agent Identity SDK verifies nonce equality; the service manages interaction state and replay policy.

Local inspection uses the same MCP exclusion:

link-cli identity credentials list --format json
link-cli identity attestations list --format json

These commands inspect local files without login or Link API calls and display metadata in both terminal and structured output. Credential inspection reports the saved ~/.link-cli/identity/credentials/current.json path, issuer, cached expiry/expired status, holder-key path/thumbprint, and claim names. Private keys are never opened; credentials, tokens, and claim values are never printed. Inspection does not modify files, verify signatures, or filter artifacts by the active account.

Attestation inspection reports paths, issuer/key identifiers, per-batch stored_token_count, aggregate total_token_count, and storage (pool or export) for JSON batches in ~/.link-cli/identity/attestations. Counts describe stored tokens; external usage is untracked and AATs have no embedded expiry. Empty stores return empty lists. Lists include per-file errors alongside valid entries.

Spend request lifecycle

A spend request moves through: create → request approval → approved (with credentials).

Required fields for a regular card create: merchant_name, merchant_url, context, and amount. payment_method_id is optional — if omitted, your default payment method will be used, or the first eligible one if no default is set. Shared Payment Token requests instead require network_id; Link Pay Token requests require credential_type=link_pay_token and the DOM-derived merchant_account_id, and Link supplies their canonical merchant identity. Card requests may also include --network-id to identify the merchant's network profile.

Constraints: context must be at least 100 characters; amount is an integer in the currency's smallest unit (e.g. cents) and must not exceed 50000 USD cents equivalent; currency can be any supported 3-letter ISO code. The user has 30 minutes from when approval is requested to approve.

Omitting currency uses the consumer's default spend-request currency, reported by userinfo.default_spend_request_currency (user-info retrieve in the CLI). Specify currency explicitly when using an amount from a checkout with a known currency. Amounts, line-item unit_amount values, and total amount values use the selected currency's minor units: 100 means ¥100 in JPY or $1 in USD. The client passes amounts through unchanged and performs no FX conversion. This default applies to newly created requests; existing requests retain their stored currency.

Expiration and delayed merchant charges

Use spend-request create --expires-at <timestamp> to set the expiry, including in direct CLI integrations. Accepts Unix seconds or an ISO 8601 timestamp ending in Z (UTC), such as 2027-02-01T12:00:00Z. The CLI converts ISO input to integer Unix seconds for the API. This option can only be set at creation.

# Allow a preorder charge for up to 90 days.
EXPIRES_AT=$(node -e 'console.log(Math.floor(Date.now() / 1000) + 90 * 86400)')
link-cli spend-request create \
  --merchant-name "Example Shop" \
  --merchant-url "https://shop.example.com" \
  --amount 4200 --currency usd \
  --context 'Preorder the selected book for $42 including tax and shipping. The merchant will charge when the book ships within 90 days.' \
  --expires-at "$EXPIRES_AT"

expires_at is the spend request's expiry. The API uses integer Unix seconds; the CLI and SDK responses convert it to an ISO 8601 timestamp ending in Z (UTC), such as "2027-02-01T12:00:00Z". Set it when the merchant may charge later, such as for a preorder, booking, or payment due after delivery. Choose a deadline that covers the expected charge date and explain the timing in context. Omit it for an ordinary purchase.

This supports deferred authorization: for eligible card flows, Link can wait to fund the payment until the merchant charges. The merchant controls charge timing; expires_at does not schedule a payment.

The server applies these limits when creating a request:

  • One-time requests default to 7 days and allow an explicit expiry up to 365 days.
  • Recurring requests default to their maximum, up to 1,827 days (about 5 years). Set an earlier expiry to end the spending permission sooner. recurring controls the spending-limit reset period; expires_at controls its end date.
  • An explicit expiry must be at least 3 hours in the future. Country limits apply to both request types: Japan is capped at 180 days, and an unknown account country uses a 180-day cap. The API validates the applicable limit.

Approval is still required within 30 minutes of requesting it. A later expires_at does not extend that approval window.

Follow the normal approval flow and check the current request status and returned credential valid_until before use. A later request expiry does not guarantee payment: funds and payment-method checks can still fail when the merchant charges. It also does not make a one-time SPT reusable.

Completion callbacks

For an application-owned return destination, supply --completion-callback-url and --nonce together when creating a request. Both are optional; supplying only one is rejected locally. The callback must be a valid URL of at most 2,048 characters. The nonce must be nonblank and at most 512 JavaScript/UTF-16 code units. The API enforces live-mode HTTPS and URL security rules.

Generate an opaque, unpredictable nonce, save it unchanged, and verify an exact match when your application receives the callback:

NONCE="$(openssl rand -hex 32)"
link-cli spend-request create \
  --amount 5000 \
  --currency usd \
  --context "The user asked to purchase the selected item from Example Store for $50, including all shipping costs and taxes." \
  --merchant-name "Example Store" \
  --merchant-url "https://example.com" \
  --completion-callback-url "https://agent.example.com/link/callback" \
  --nonce "$NONCE"

Use the returned approval_url and status_details.requires_action.next_action.action_url as-is. When creation-time callback context is supplied, do not manually append callback parameters. A callback does not establish approval; retrieve the spend request and check its status before continuing.

Test mode: Pass --test to create a testmode SpendRequest. A testmode SpendRequest will return test payment credentials (e.g test card 4000009990001984) rather than a real payment credential. Testmode SpendRequests will not charge the underlying payment method of the SpendRequest. This is useful for development and integration testing without real payment methods.

# Update before approval
link-cli spend-request update lsrq_001 \
  --merchant-url https://press.stripe.com/working-in-public

# Request approval separately (alternative to create --request-approval)
link-cli spend-request request-approval lsrq_001

# Retrieve a request; request a credential only when needed
link-cli spend-request retrieve lsrq_001

# Cancel a spend request (from created, pending_approval, or approved state)
link-cli spend-request cancel lsrq_001

Use spend-request retrieve <id> --interval 2 to wait for the initial status to change. Polling starts only for created, pending_approval, or requires_action with an auto_resume resolution. Other statuses, including submitted and unfamiliar API values, return immediately. A change to another waiting status also returns; inspect the result and retrieve again as needed. If --timeout or --max-attempts is reached without a change, the command exits non-zero with POLLING_TIMEOUT.

Recurring payments

For a subscription or other recurring purchase, confirm the spending limit and cadence with the user, then add --recurring-interval when creating the request:

link-cli spend-request create \
  --credential-type card \
  --merchant-name "Example Pro" \
  --merchant-url "https://example.com/subscribe" \
  --amount 2000 \
  --currency usd \
  --recurring-interval month \
  --context "Monthly access to Example Pro requested by the user, with a spending limit of $20 per month including all taxes."

Supported intervals are day, week, month, and year. --recurring-interval-count is a positive integer and defaults to 1 on the server. For example, --recurring-interval week --recurring-interval-count 2 resets the limit every two weeks. The count requires an interval. Omit both flags for a one-time purchase.

For recurring requests, --amount is the spending limit per period, in the currency's minor units. It is not the total budget for the subscription's lifetime. The merchant controls billing; the CLI does not schedule charges. The same recurring options are available with card, shared_payment_token, and link_pay_token; each credential type keeps its merchant targeting and checkout requirements. Link enforces availability and applicable limits.

Approval and credential retrieval use the normal flow. Responses include recurring.interval and recurring.interval_count; spend-request list also shows the cadence. Recurring card requests remain active after individual charges. Do not wait for the request to become succeeded after each payment or create a new request for each period. Confirm each payment from the merchant's result or transaction records.

Recurring terms are set at creation; spend-request update does not change the interval or count. Respect the returned credential expiry. To stop future use of the payment credential, run link-cli spend-request cancel <id> and check the result. This revokes the credential; it does not cancel the subscription with the merchant. Complete the merchant's cancellation flow when the user asks to cancel the subscription.

Credential types

By default, a spend request provisions a virtual card. Link can also provide a shared payment token (SPT) for use with the Machine Payment Protocol (MPP) or a Link Pay Token (LPT) for supported Stripe payment surfaces. LPT is a distinct credential type.

Shared Payment Token

For merchants that support the Machine Payments Protocol (HTTP 402) and the Stripe payment method, instead pass --credential-type "shared_payment_token" when creating the spend request. SPTs for one-time requests are single use; a consumed token requires a new spend request.

For recurring SPTs, usage_limits.max_amount is the spending limit per period. The available spending amount resets to this limit each new period, as defined by recurring.interval and recurring.interval_count.

Use mpp decode to validate a raw WWW-Authenticate header and extract the network_id needed for shared_payment_token spend requests:

link-cli mpp decode \
  --challenge 'Payment id="ch_001", realm="merchant.example", method="stripe", intent="charge", request="..."'

Use mpp pay to complete purchases:

link-cli mpp pay https://climate.stripe.dev/api/contribute \
  --spend-request-id lsrq_001 \
  --method POST \
  --data '{"amount":100}' \
  --header "X-Custom: value"

Link Pay Token

Some Stripe payment surfaces expose an AI-agent steering block that supports a Link Pay Token (LPT). Inspect the checkout in a browser before creating the SpendRequest: find the .AiAgentPaymentSteering block and verify that both input[name="link_pay_token"] and data-stripe-merchant-account="acct_..." are present in the same Stripe frame.

Create an LPT-bound request with the DOM-derived account ID. Do not pass --merchant-name or --merchant-url; Link resolves the canonical merchant identity from the account ID for the approval screen.

link-cli spend-request create \
  --credential-type link_pay_token \
  --merchant-account-id acct_... \
  --payment-method-id csmrpd_xxx \
  --context "Purchasing an item from the checkout the agent inspected. The user initiated this purchase through the shopping assistant." \
  --amount 3500 \
  --request-approval

LPT requests do not support --test, --network-id, merchant name, or merchant URL. After approval, retrieve --include link_pay_token immediately before using it on the same checkout surface. Each returned LPT is valid for up to 30 minutes, or until the SpendRequest expires. If either DOM marker is absent, create a regular virtual card SpendRequest instead; do not create an LPT request.

Limits

Limit Value
Max amount per spend request $500 (50,000 cents)
Approval window 30 minutes — user must approve within 30 min of request-approval
Spend request expiry (expires_at) 7 days by default for one-time requests; up to 365 days one-time or 1,827 days recurring, subject to country limits
Credential validity Check returned valid_until and current request status
Daily spend $500
Monthly spend (30 days) $20,000
Concurrent active requests (created + approved) 30
Concurrent approved requests 10
Hourly creation rate 50 per hour
Rolling creation rate 200 per 60 days

Dollar limits are enforced as the USD equivalent for spend requests in other currencies.

Use mpp pay to complete purchases on merchants that use the Machine Payments Protocol. The spend request must use credential_type: "shared_payment_token" and you must approve it before paying. Requests created by mpp pay are for one-time payments; consumed SPTs require a new spend request.

link-cli mpp pay https://climate.stripe.dev/api/contribute \
  --spend-request-id lsrq_001 \
  --method POST \
  --data '{"amount":100}' \
  --header "X-Custom: value"

In agent mode (--format json), the full flow returns _next.pay_argv ({ "command": "mpp", "args": [...] }) to complete payment. If the new spend request is already approved, it omits polling instructions and _next.poll_argv. Otherwise, it also returns _next.poll_argv to wait for approval before paying. Invoke continuations directly without a shell. command is a Link CLI subcommand, not an executable; pass it and each args entry as separate arguments to your trusted CLI entrypoint:

import { execFile } from 'node:child_process';

const { command, args } = result._next.pay_argv;
// cliScript is the trusted path to the installed CLI's dist/cli.js.
execFile(process.execPath, [cliScript, command, ...args], { shell: false });

For the standalone Windows executable, use execFile(cliExecutable, [command, ...args], { shell: false }). Avoid npm .cmd shims, which require a command shell. Never join, unquote, or re-split an argv array to construct a shell string: URLs, request bodies, and headers can contain merchant-controlled text.

Outside Windows, _next.pay_command and _next.poll_command remain available with _next.shell: "posix". These strings are quoted only for POSIX-compatible shells; pass them verbatim only to such a shell, regardless of where the output was generated. They are unsafe for cmd.exe and PowerShell and are omitted on Windows.

Spend-request approval and automatic-resume polling similarly return _next.argv. UCP checkout creation returns _next.argv as a completion template: after approval, replace only the <spend_request_id> argument with the approved ID, preserve the original business argument, and invoke the CLI directly. Do not execute the template before replacement or edit its compatibility string. These flows retain _next.command with _next.shell: "posix" outside Windows and omit the string on Windows.

Use mpp decode to validate a raw WWW-Authenticate header and extract the network_id needed for shared_payment_token spend requests:

link-cli mpp decode \
  --challenge 'Payment id="ch_001", realm="merchant.example", method="stripe", intent="charge", request="..."'

UCP checkout

Search a catalog, then use the returned business and SKU to create a UCP checkout:

link-cli ucp catalog search --query "running shoes" --format json
link-cli ucp checkout create --business <business> \
  --line-item 'id:<sku>' \
  --buyer 'first_name:Jane,email:jane@example.com,phone_number:+1234567890' \
  --fulfillment '{"methods":[{"type":"shipping","destinations":[{"street_address":"123 Main St","address_locality":"Springfield","address_region":"IL","postal_code":"62701","address_country":"US"}]}]}' \
  --format json

--line-item is repeatable; quantity defaults to 1. All buyer fields are optional (first_name, last_name, email, phone_number). --fulfillment accepts the entire UCP JSON object; --fullfilment is an alternate spelling. Omit buyer or fulfillment to use saved Link details during creation. The seller determines the checkout currency.

Review the returned messages, totals, and fulfillment choices. Update the checkout as needed. Updates use PUT. Line items, buyer, and fulfillment are optional on update: omitted sections keep their existing checkout values. Updates do not reload saved Link wallet details.

link-cli ucp checkout retrieve --id <checkout_id> --business <business> --format json
link-cli ucp checkout update --id <checkout_id> --business <business> \
  --buyer 'email:jane@example.com' \
  --fulfillment '<entire fulfillment JSON object>' --format json

Once the checkout is ready_for_complete, create and approve a shared_payment_token spend request for the final totals entry with type: "total", using the business as its --network-id. Complete with that spend request ID, then retrieve payment state or poll:

link-cli ucp checkout complete <checkout_id> --business <business> \
  --spend-request-id <spend_request_id> --format json
link-cli ucp checkout retrieve --id <checkout_id> --business <business> \
  --spend-request-id <spend_request_id> --poll --format json

Payment succeeds when the checkout is completed and the spend request is succeeded. Follow any required payment action and continue polling; avoid repeating completion. Checkout commands use the UCP API's live flow. Only catalog search supports --test demo results.

To cancel a checkout, supply its ID and business. The response contains the canceled checkout:

link-cli ucp checkout cancel --id <checkout_id> --business <business> --format json

Report outcomes

Use report to record the outcome of a purchase attempt. Reporting is optional, but calling it after attempts — success or failure — helps Stripe improve checkout for agents.

# Successful purchase
link-cli report --domain shop.example.com --outcome success --spend-request-id lsrq_abc123

# Blocked by captcha
link-cli report --domain shop.example.com --outcome blocked --spend-request-id lsrq_abc123 \
  --tag captcha --step "checkout page" --freeform-context "Turnstile challenge appeared"

# Abandoned due to timeout
link-cli report --domain shop.example.com --outcome abandoned --spend-request-id lsrq_abc123 \
  --tag timeout

# With a step-by-step account of the path taken
link-cli report --domain shop.example.com --outcome success --spend-request-id lsrq_abc123 \
  --attempt-trace '1. / — clicked "Shop" → category grid
2. /checkout — email required before shipping unlocks; entered [email]
3. /checkout — clicked "Pay now" → order confirmed'

Outcomes: success, blocked, abandoned. Tags: 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, other.

--step records where the agent was when the outcome occurred. --attempt-trace is the whole path: one numbered line per step, each giving the URL path, the label acted on, the action, and the observed result, so another agent can follow it. Send it on failures too — the dead ends are the useful part. Omit the buyer's personal data and write [email], [address] in its place. Over 8000 characters is truncated by the server rather than rejected, so send the full narrative instead of skipping the report.

Handle step-ups

Actions can appear at creation or on later retrieves, under status_details.requires_action.next_action or status_details.failed.next_action. Inspect the current action with:

link-cli spend-request retrieve lsrq_001 --next-action --format json

The JSON output is an array containing one projected result, for example:

[
  {
    "next_action": {
      "type": "ssn_verification",
      "resolution": "create_new_spend_request_after_completion",
      "display_message": "Additional identity verification is required.",
      "action_url": "https://example.com/verify?step=ssn",
      "expires_at": null
    }
  }
]

Values come from the server; the example URL is synthetic. The action's expiry is a string or null, and older responses may omit it. [{"next_action": null}] means no action is exposed in this snapshot; it does not establish approval or rule out later verification. A missing/inaccessible request still returns an error. Snapshot mode neither forces verification nor completes or approves a request, and adds no continuation instructions.

Show the server's message and exact non-null URL to the user, who completes verification themselves. Follow resolution: for auto_resume, keep the same request and inspect it after each user completion. A Visa OTP action can become partner_create_card_enrollment without changing status or URL. Use another snapshot or ordinary retrieve without polling to inspect the latest action; ordinary retrieve with --interval waits for status changes. For create_new_spend_request or create_new_spend_request_after_completion, follow the indicated action and user-completion requirements, then create a new request. Never attempt to resume a terminal failed request.

Line items and totals

Optionally pass additional data on the specific items being purchased, and any tax, shipping or other amounts.

--line-item and --total use repeatable key:value format.

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

--line-item "name:Running Shoes,unit_amount:12000,quantity:1,description:Trail runners"

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

--total "type:subtotal,display_text:Subtotal,amount:12000" \
--total "type:total,display_text:Total,amount:12000"

Metadata

Attach arbitrary string data to a spend request with the repeatable --metadata flag (key:value format). Max 50 keys, key ≤ 40 chars, value ≤ 500 chars.

link-cli spend-request create ... \
  --metadata "order_id:ord_123" \
  --metadata "team:growth"

In MCP/agent mode, pass metadata as a structured { key: value } object.

Environment variables

Variable Effect
LINK_AUTH_FILE Same as --auth — override the auth credential file path (flag takes precedence)
LINK_ACCESS_TOKEN Use this access token directly, bypassing auth storage
LINK_REFRESH_TOKEN Refresh token to use when LINK_ACCESS_TOKEN is expired
LINK_NO_REFRESH When set, never auto-refresh the access token — error instead
LINK_API_BASE_URL Override the API base URL
LINK_AUTH_BASE_URL Override the auth base URL
LINK_HTTP_PROXY Route all requests through an HTTP proxy (requires undici)

Testing agent handling with sample spend-request responses

spend-request simulate is opt-in engineering evaluation tooling. Log in with link-cli auth login or configure LINK_ACCESS_TOKEN first. It currently returns local fixtures; a backend provider will replace them after the endpoints land. It creates no spend request, verification session, OTP, approval, or account change.

link-cli spend-request simulate --scenario ssn_verification --format json
link-cli spend-request simulate --scenario visa_otp --format json
link-cli spend-request simulate --list-scenarios --format json

Choose exactly one selector. Use discovery for available IDs and aliases. visa_otp currently aliases partner_auth_challenge. Samples yield one {"next_action": ...} object (or null) inside the JSON array; discovery yields one {"catalog_version": 1, "data": [...]} object. The warning goes to stderr. Synthetic URLs under example.invalid cannot complete verification. Null/absent URLs still require presenting the message; a null overall action proves no approval.

The evaluation controller selects scenarios. Present the exact URL/message and follow resolution: auto_resume keeps the same request; create_new_spend_request describes a new request; create_new_spend_request_after_completion requires user completion first; reauthorize_with_additional_scope describes obtaining the required scope without inventing an auth URL or executing an auth upgrade during evaluation. Never use simulation as a fallback when a real payment fails. It is excluded from MCP discovery.

The backend transport has not shipped: local fixture results do not establish backend availability, authentication scope enforcement, or a real lifecycle. The later backend provider must report an unavailable endpoint as an error and must not fall back to fixtures. See the engineering guide for the catalog and evaluation/transition checks.

Integrating into agents

If you are building an agent and want to offer Link as a native experience to your consumers (as a connector, plugin, pre-installed capability etc.), please look at our documentation and steps for integrating. We can support higher limits, more embedded approval flows, and additional capabilities.

SDKs

For services accepting agents, Agent Identity verifies Link attestations and identity presentations. The wallet API clients below obtain credentials and call Link APIs.

Applications can use the credential-only Link client directly in TypeScript, Go, or Python. The Python SDK provides synchronous and asynchronous clients covering the Go SDK's API resources with Python conventions. Authentication flows and credential persistence remain the embedding application's responsibility.

The TypeScript SDK also exports reusable agent tools. Use the Eve extension to mount them in an Eve agent with a static access token or an application-provided Eve auth provider, plus a wallet skill.

Onboarding and Demos

Run the guided setup flow — authenticates, checks payment methods, shows the app download QR, and runs both demo flows:

link-cli onboard

Run an interactive demo of both Link payment flows (always uses test mode — no real charges):

link-cli demo              # shows menu to choose flow
link-cli demo --only-card  # virtual card flow only
link-cli demo --only-spt   # machine payment (SPT) flow only

Development

Workspace development requires Node.js 24+.

pnpm install
pnpm run build
pnpm run link-cli --help

Watch mode:

pnpm run dev

Run tests:

pnpm run test

This runs the TypeScript, Go, and Python suites. Go SDK development requires Go 1.23 or newer. Python SDK development uses uv and requires Python 3.11 or newer; uv can install the interpreter for you.

uv python install 3.11
uv sync --directory packages/sdk-python --locked
pnpm run test:python
pnpm run check:python

Type-check and lint:

pnpm run typecheck
pnpm biome check .

Releasing

This project uses Changesets to version and publish @stripe/link-cli, @stripe/link-sdk, and @stripe/link-integrations-eve. @stripe/link-typescript-config is private and is not published.

Add a changeset

Add a changeset for any user-facing change before merging:

pnpm changeset

Select each affected public package and its semver bump. Commit the generated file in .changeset/ with the change.

Publish

After changesets reach main, the release workflow creates or updates the Version Packages pull request. Review and merge that PR to publish the new package versions to npm. The workflow uses npm trusted publishing and generates provenance attestations for the SDK; no npm token or local publish command is needed.

CLI releases also upload the bundled JavaScript, standalone executables, and checksums to the corresponding GitHub Release. SDK-only releases skip that CLI artifact work.

To inspect the packages without publishing them:

pnpm turbo run build
pnpm --filter @stripe/link-cli --filter @stripe/link-sdk --filter @stripe/link-integrations-better-auth --filter @stripe/link-integrations-eve publish --dry-run --no-git-checks

CI runs the same publish dry-run for every pull request.

The Go SDK is a nested module and is released with a repository tag such as packages/sdk-go/v0.1.0. Go module tags are independent of npm package versions.

The Python SDK is published to PyPI as stripe-link-sdk using GitHub OIDC Trusted Publishing, with attestations for its wheel and source distribution. Its versions are independent of npm and Go: update project.version in packages/sdk-python/pyproject.toml, refresh uv.lock, and add Python changelog notes in a PR. Merging to main publishes an unpublished version automatically after the Python checks pass. See the Python release guide for one-time PyPI setup, verification, and recovery.

About

Let your agents spend on your behalf. Your payment credentials are never exposed. You approve every purchase.

Resources

Code of conduct

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages