Set up OAuth for Link CLI
Connect your agent to your customer's Link account by registering an OAuth client and implementing the authorization flow.
Before your agent can retrieve payment credentials or financial data, it needs an access token from a customer. Hosted agents use the authorization code flow described here, which uses a registered OAuth client.
To authorize a local agent running on your own machine, use the device authorization flow instead. See the link-cli repository.
Before you begin
Before you begin, make sure that:
- You have a Stripe account .
- Your agent is a hosted service that you operate on behalf of your customers.
- Your customers are US consumers. Your own business can be outside the US.
- You’re familiar with OAuth 2.0 authorization code flow concepts.
Both authorization flows authenticate against login.link.com and produce an access token that Link CLI reads from the related setting. The difference is who holds the client credentials. With device authorization, Link CLI uses a built-in public client, so there’s nothing for you to register.
Register a confidential OAuth client
A hosted agent requires a confidential OAuth client to securely authenticate and manage customer authorization. Unlike public clients, a confidential client stores credentials server-side—appropriate when you control the backend.
To register:
- Contact Stripe to request an OAuth client from Link .
- Complete the registration form that Stripe sends you.
- Stripe provides your client _ id and client _ secret .
- Store both values securely on your server.
Protect your client credentials
Never embed client_secret in client-side code, agent skill files, or version control. Store it in a secrets manager accessible only to your backend.
Construct the authorization URL
Redirect the customer to the Link 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 each parameter value. The scope parameter takes a space-separated list, so encode the separators as %20.
Parameters
The authorization URL has the following parameters:
| Parameter | Required | Description |
|---|---|---|
key | Yes | Your Stripe publishable key. |
client_id | Yes | Your OAuth client ID. |
redirect_uri | Yes | The URI where Link redirects the customer after authorization. Must match the URI registered with your OAuth client. |
response_type | Yes | Must be code. |
scope | Yes | Space-separated list of OAuth scopes. See Available scopes. Scope delimiters aren’t uniform— payment_methods.agentic uses a period, and userinfo:read uses a colon. |
state | Yes | A random string to prevent the related setting attacks. Verify this value when the customer returns to your redirect URI. |
code_challenge | Yes | A the related setting code challenge. Generate a random code_verifier (43–128 characters, URL-safe), then compute the related setting(the related setting(code_verifier)). |
code_challenge_method | Yes | Must be the related setting. |
authorization_details | Conditional | Required for financial insights. A JSON array specifying the data access your agent needs. See Add financial insights to your agent for the required actions. |
Available scopes
Request only the scopes your agent uses. The set of available scopes grows over time.
| Scope | Grants access to |
|---|---|
payment_methods.agentic | Creating spend requests to retrieve one-time-use payment credentials. |
userinfo:read | Basic customer information, which you can use to prefill checkout and to check whether a customer is eligible before starting a purchase. |
Financial insights uses the authorization_details parameter rather than a scope. See Add financial insights to your agent.
After the customer authenticates and approves, Link redirects to:
Redirect response
https://yourapp.com/callback?code=AUTHORIZATION_CODE&state=YOUR_STATE_VALUE
Your backend must:
- Verify that state matches the value you sent.
- Extract the code parameter.
- Exchange the code for tokens within 10 minutes (codes are single-use).
Exchange the code for tokens
To exchange the code for tokens, make a request to the authorization endpoint. The Authorization header contains the Stripe publishable key associated with your integration. client_secret remains in the request 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"
}
Store both tokens securely on your server.
Token lifecycle
The token response includes the following tokens:
| Token type | Lifetime | Behavior |
|---|---|---|
| Access token | 1 hour | Refresh before expiry using the refresh token. |
| Refresh token | 1 year | Rotated on each use. Store the new refresh token from each response. |
Refresh an access token
To refresh an access token, make a request to the authorization endpoint:
Refresh request
The response includes a new access_token and a rotated refresh_token. Store the new refresh token, because the previous refresh token is invalidated.
Configure Link CLI with your token
Set the access token as an environment variable so Link CLI can authenticate requests:
Set token
export LINK_ACCESS_TOKEN=liwltoken_...
Verify the connection:
Verify authentication
link-cli user-info retrieve
For production, inject the related setting from your secrets manager at runtime.
Revoke access
To revoke a customer’s access when they disconnect from your agent:
Revoke token
Security requirements
To secure your OAuth implementation:
- Always use the related setting ( code _ challenge _ method=the related setting ) in the authorization request.
- Always validate the state parameter on the OAuth callback.
- Store client _ secret server-side only.
- Store refresh tokens securely server-side.
- Refresh access tokens before expiry instead of waiting for 401 responses.
- Exchange authorization codes within 10 minutes (they’re single-use).
