# Authorize your agent with Link Agent Wallet

Register an OAuth client and implement the authorization flow to connect your agent to your customer's account.

Before your agent can retrieve payment credentials or financial data, it needs access to a customer’s Link account. Hosted agents use the authorization code flow described here, which uses a registered OAuth client.

## Before you begin

Before you begin, make sure that:

- You have a [Stripe account](https://dashboard.stripe.com/register).
- Your customers must be based in the US or Canada. If you use [financial insights](https://docs.stripe.com/financial-connections/agents/financial-insights.md), your customers must be based in the US. Your business can be based outside the US.
- You’re familiar with the [OAuth 2.0 authorization code flow](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1).

## Register an OAuth client

A hosted agent needs a confidential OAuth client, which stores its credentials server-side. Registration also controls what your customers see when they approve the connection: a registered client shows your name and logo instead of a generic one.

1. Apply for an OAuth client with the [Link Agent Wallet application form](https://docs.google.com/forms/d/1frwbtMaUUAaMMv0rHEk_qCvKUHJUxHCr8vkLknnBS9w/viewform).
2. Provide your application name, description, and the exact redirect URIs your agent uses. Redirect URIs are matched exactly at authorization time, so register every one you need.
3. Stripe sends you a `client_id` and `client_secret`.
4. Store both on your server.

> #### Protect your client credentials
> 
> Never expose your `client_secret` in client-side code, agent skill files, prompts, or version control. Store it in a secrets manager that only your back end can read.

## Send the customer to Link

Redirect the customer to the authorization endpoint to request access to their wallet:

```url
https://login.link.com/auth?key=pk_live_YOUR_PUBLISHABLE_KEY&client_id=YOUR_CLIENT_ID&redirect_uri=https://yourapp.com/callback&response_type=code&scope=payment_methods.agentic%20userinfo:read&state=RANDOM_STATE_VALUE&code_challenge=YOUR_PKCE_CODE_CHALLENGE&code_challenge_method=S256
```

URL-encode every parameter value.

| Parameter | Required | Description |
| --- | --- | --- |
| `key` | Yes | Your Stripe publishable key. |
| `client_id` | Yes | The client ID from registration. |
| `redirect_uri` | Yes | Where Link sends the customer afterward. It must match a URI registered with your client. |
| `response_type` | Yes | Must be `code`. |
| `scope` | Yes | The permissions you’re requesting. Separate multiple scopes with spaces (encoded as `%20`) or commas. See [Scopes](https://docs.stripe.com/agentic-commerce/agents/link-agent-wallet/oauth.md#scopes). |
| `state` | Yes | A random string that prevents CSRF. Verify it when the customer returns. |
| `code_challenge` | No | A PKCE code challenge. Optional for confidential clients, but recommended. Generate a random `code_verifier` of 43 to 128 URL-safe characters, then compute `BASE64URL(SHA256(code_verifier))`. |
| `code_challenge_method` | Conditional | Required if you send `code_challenge`. Must be `S256`. Any other value is rejected. |
| `authorization_details` | Conditional | Required for [financial insights](https://docs.stripe.com/financial-connections/agents/financial-insights.md). A JSON array naming the data your agent needs. |

### Scopes 

Request only what your agent uses.

| Scope | Grants access to |
| --- | --- |
| `payment_methods.agentic` | Creating spend requests and retrieving the resulting payment credentials. Required for every payment integration. |
| `userinfo:read` | The customer’s name and email. Use it to prefill checkout forms and to check eligibility before you start a purchase. |
| `financial_insights.sources.read` | The list of connected accounts and their capabilities. |
| `financial_insights.balances.read` | Current balances on connected accounts. |
| `financial_insights.transactions.read` | Transaction history from connected accounts. |

The three `financial_insights` scopes map to the matching `authorization_details` source actions. For financial insights integrations, use `authorization_details` as described in [Add financial insights to your agent](https://docs.stripe.com/financial-connections/agents/financial-insights.md)—Link reconciles the two forms before showing the customer a consent screen.

### Handle the redirect 

After the customer authenticates and approves, Link redirects to your `redirect_uri`:

```text
https://yourapp.com/callback?code=AUTHORIZATION_CODE&state=YOUR_STATE_VALUE
```

Then, your back end:

1. Verifies that `state` matches the value you sent.
2. Reads the `code` parameter.
3. Exchanges the code within 10 minutes. Codes are single-use.

If the customer declines, or isn’t eligible for a scope you asked for, Link returns an error to your redirect URI instead of a code.

## Exchange the code for tokens

Exchange the authorization code at the token endpoint. The `Authorization` header contains your Stripe publishable key. `client_secret` remains in the body as the OAuth client credential.

```bash
curl -X POST https://login.link.com/auth/token \
  -H "Authorization: Bearer pk_live_YOUR_PUBLISHABLE_KEY" \
  -d "grant_type=authorization_code" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "redirect_uri=https://yourapp.com/callback" \
  -d "code=AUTHORIZATION_CODE" \
  -d "code_verifier=YOUR_PKCE_CODE_VERIFIER"
```

```json
{
  "access_token": "liwltoken_...",
  "refresh_token": "liwlrefresh_...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "payment_methods.agentic userinfo:read"
}
```

The response echoes the `scope` and `authorization_details` that the customer actually granted. Read it instead of relying only on the requested scopes, because a customer can approve a subset.

### Refreshing tokens 

Access tokens are short-lived and `expires_in` indicates how long the token is valid. Refresh ahead of expiration instead of waiting for a `401`:

```bash
curl -X POST https://login.link.com/auth/token \
  -H "Authorization: Bearer pk_live_YOUR_PUBLISHABLE_KEY" \
  -d "grant_type=refresh_token" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "refresh_token=CURRENT_REFRESH_TOKEN"
```

Refresh tokens rotate. Every response contains a new `refresh_token` and invalidates the one you sent, so persist the new value immediately.

## Use the token

Use an environment variable to expose the access token to the CLI and SDK:

```bash
export LINK_ACCESS_TOKEN=liwltoken_...
```

In production, inject `LINK_ACCESS_TOKEN` from your secrets manager at runtime. To let the CLI refresh on its own, also set `LINK_REFRESH_TOKEN`. Setting `LINK_NO_REFRESH` instructs the CLI to raise an error instead of refreshing, which is what you want when your application owns refresh.

## Revoke access

When a customer disconnects the Link Agent Wallet from your agent, revoke the grant in addition to deleting your copy of the tokens:

```bash
curl -X POST https://login.link.com/auth/revoke \
  -H "Authorization: Bearer pk_live_YOUR_PUBLISHABLE_KEY" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "token=TOKEN_TO_REVOKE" \
  -d "token_type_hint=refresh_token"
```

Revoking the refresh token ends the grant, which invalidates the access tokens issued from it.

## Security requirements 

- Always send PKCE with `code_challenge_method=S256`.
- Always validate `state` on the callback.
- Keep `client_secret` and refresh tokens server-side only.
- Refresh access tokens before they expire.
- Exchange authorization codes within 10 minutes.
- Read the granted `scope` from the token response instead of assuming your full request was approved.

## See also

- [Enable agents to spend](https://docs.stripe.com/agentic-commerce/agents/link-agent-wallet/use-link-wallet-pay-online.md)
- [Add financial insights to your agent](https://docs.stripe.com/financial-connections/agents/financial-insights.md)
