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 .
- Your customers are US or Canadian consumers, or US consumers if you use financial insights . Your own business can be outside the US.
- You’re familiar with the OAuth 2.0 authorization code flow .
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.
- Apply for an OAuth client with the Link Agent Wallet application form .
- 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.
- Stripe sends you a client _ id and client _ secret .
- 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:
Authorization request
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. |
state | Yes | A random string that prevents the related setting. Verify it when the customer returns. |
code_challenge | Yes | A the related setting code challenge. Generate a random code_verifier of 43 to 128 URL-safe characters, then compute the related setting(the related setting(code_verifier)). |
code_challenge_method | Yes | Must be the related setting. Any other value is rejected. |
authorization_details | Conditional | Required for financial insights. 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 — 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:
Redirect response
https://yourapp.com/callback?code=AUTHORIZATION_CODE&state=YOUR_STATE_VALUE
Then, your back end:
- Verifies that state matches the value you sent.
- Reads the code parameter.
- 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.
Token exchange
Token response
{
"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:
Refresh request
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:
Set the token
export LINK_ACCESS_TOKEN=liwltoken_...
In production, inject the related setting from your secrets manager at runtime. To let the CLI refresh on its own, also set the related setting. Setting the related setting 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:
Revoke a token
Revoking the refresh token ends the grant, which invalidates the access tokens issued from it.
Security requirements
- Always send the related setting with code _ challenge _ method=the related setting .
- 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.