Integrate the Embedded Components onrampPrivate preview
Step-by-step integration guide for the Embedded Components onramp.
This guide walks you through integrating the Embedded Components onramp into your mobile app with minimal Stripe branding.
Before you begin
The Embedded Components onramp is only available in the US (excluding Hawaii).
The Embedded Components API is in private preview. To request access:
After you get access to the Embedded Components onramp, obtain your secret and publishable API keys from the API keys page.
Mobile SDK configuration
Step 1: Install the Stripe React Native SDK
Follow the installation instructions in the README to install the Stripe React Native SDK.
Step 2: Add the onramp dependency Client-side
By default, the onramp dependency isn’t included in the Stripe React Native SDK to reduce bundle size. Include it as follows, depending on your platform.
Step 3: Use StripeProvider Client-side
Wrap your app with StripeProvider at a high level so Stripe functionality is available throughout your component tree. Key properties:
publishableKey– Your Stripe publishable key.merchantIdentifier– Your Apple Merchant ID (required for Apple Pay).urlScheme– Required for return URLs in authentication flows.
This component is essential for initializing the Stripe SDK in your React Native application before using payment-related features.
import { StripeProvider } from '@stripe/stripe-react-native'; function App() { return ( <StripeProvider publishableKey="pk_test_..." merchantIdentifier="merchant.identifier" urlScheme="your-url-scheme" > {/* Your app components */} </StripeProvider> ); }
Step 4: Configure the onramp SDK Client-side
Before any onramp APIs can be called successfully, you’ll need to configure the SDK using the configure method. It’s provided by the useOnramp() hook. configure takes an instance of Onramp. to customize your business display name and lightly customize elements in Stripe-provided interfaces, such as the user’s wallet, one-time passcode authorization, and identity verification UI.
import { useOnramp } from '@stripe/stripe-react-native'; function OnrampComponent() { const { configure } = useOnramp(); const setupOnramp = useCallback(async () => { const result = await configure({ merchantDisplayName: 'My Crypto App', appearance: { lightColors: { primary: '#2d22a1', contentOnPrimary: '#ffffff', borderSelected: '#07b8b8' }, darkColors: { primary: '#800080', contentOnPrimary: '#ffffff', borderSelected: '#526f3e' }, style: 'ALWAYS_DARK', primaryButton: { cornerRadius: 8, height: 48 } } }); if (result.error) { console.error('Configuration failed:', result.error.message); } }, []); React.useEffect(() => { setupOnramp(); }, []); return null; }
Authentication
Step 1: Check for a Link account Client-side
The user must have a Link account to use the onramp APIs. Use hasLinkAccount to determine if the user’s email is associated with an existing Link account. See the HasLinkAccountResult for the return type and the OnrampError for the error type.
- If they have an account, proceed to Authorize.
- If they don’t, use Register a new Link user, then proceed to Authorize.
const { hasLinkAccount, registerLinkUser, authorize } = useOnramp(); const linkResult = await hasLinkAccount('user@example.com'); if (linkResult.error) return; if (linkResult.hasLinkAccount) { // Proceed to authorization. } else { // Register the user first (see next step). }
View example
function AuthComponent() { const { hasLinkAccount, registerLinkUser } = useOnramp(); const handleAuth = useCallback(async () => { const linkResult = await hasLinkAccount('user@example.com'); if (linkResult.error) return; if (linkResult.hasLinkAccount) { // Proceed to authorization. } else { const userInfo = { email: 'user@example.com', phone: '+12125551234', country: 'US', fullName: 'John Smith', }; const registerResult = await registerLinkUser(userInfo); if (registerResult.error) { } else if (registerResult.customerId) { // Proceed to authorization. } } }, []); return <Button title="Authenticate" onPress={handleAuth} />; }
Step 2: Register a new Link user (if needed) Client-side
If the user doesn’t have a Link account, use registerLinkUser to create one with user information collected from your UI. Upon successful account creation, proceed to Authorize. See the RegisterLinkUserResult for the return type and the OnrampError for the error type.
const userInfo = { email: 'user@example.com', phone: '+12125551234', country: 'US', fullName: 'John Smith', }; const registerResult = await registerLinkUser(userInfo); if (registerResult.error) return; if (registerResult.customerId) { // Proceed to authorization. }
Step 3: Authorize Client-side Server-side
The primary method of authentication is via two-factor authorization.
3a. Create a LinkAuthIntent
A LinkAuthIntent tracks scopes of the OAuth requests and the status of user consent. Your backend calls the Create a LinkAuthIntent API with the onramp OAuth scopes, get authIntentId, and send it to the client.
OAuth scopes used when creating a LinkAuthIntent:
| Scope (string) | Description |
|---|---|
kyc. | Read the user’s KYC verification status. |
crypto:ramp | Add crypto wallets to deposit from the user’s account on their behalf. |
3b. User consents
The client calls authorize with the authIntentId to complete consent. The authorize SDK looks up and verifies the user’s Link session, shows them what your app is requesting (the OAuth scopes) on a consent screen or inline on the OTP screen, and collects their approval.
The SDK then sends that consent to Stripe so your backend can exchange the intent for an access token and finish the flow. The result includes a customerId that must be used for all subsequent onramp API calls. See the AuthorizeResult for the return type and the OnrampError for the error type.
const result = await authorize(authIntentId); if (result?.error) { // An error occurred. Stop here. } else if (result?.status === 'Consented' && result.customerId) { // Authorization successful; exchange for access token. } else if (result?.status === 'Denied') { // The user denied authorization. Stop here. } else { // The user canceled authorization. Stop here. }
3c. Request access tokens
If the result is Consented, your backend calls the Retrieve Access Tokens API to request access tokens. Store the access token and use it on all subsequent onramp API requests (for example, in the Stripe-OAuth-Token header).
# Request curl -X POST https://login.link.com/v1/link_auth_intent/{authIntentId}/tokens \ -H "Authorization: Bearer $STRIPE_SECRET_KEY" # Response { "access_token": "liwltoken_xxx", "token_type": "Bearer", "expires_in": 3600, "refresh": { "refresh_token": "liwlrefresh_xxx", "expires_in": 7776000 } }
Identity
Step 1: Check if KYC collection is needed Server-side
Your backend calls the Retrieve a CryptoCustomer API with the customerId. Inspect the response verifications array. If it includes an entry with type kyc_ and status not_, proceed to Collect KYC.
curl https://api.stripe.com/v1/crypto/customers/{customerId} \ -H "Authorization: Bearer $STRIPE_SECRET_KEY" \ -H "Stripe-OAuth-Token: $ACCESS_TOKEN"
View example
async function getCryptoCustomer(req, res) { const { id } = req.params; const oauthToken = req.headers['stripe-oauth-token']; const response = await fetch( `https://api.stripe.com/v1/crypto/customers/${id}`, { headers: { Authorization: `Bearer ${process.env.STRIPE_SECRET_KEY}`, 'Stripe-OAuth-Token': oauthToken ?? '', }, } ); const customer = await response.json(); const verifications = customer.verifications ?? []; const kycVerified = verifications.find((v) => v.name === 'kyc_verified'); const idDocVerified = verifications.find( (v) => v.name === 'id_document_verified' ); res.json({ customerId: customer.id, providedFields: customer.provided_fields ?? [], kycStatus: kycVerified?.status ?? 'not_started', idDocStatus: idDocVerified?.status ?? 'not_started', }); }
Step 2: Collect KYC (if needed) Client-side
If the customer needs KYC verification, your client calls attachKycInfo to collect and submit user KYC data. Present your own interface to the user to collect this KYC information. See the KycInfo for the return type and the OnrampError for the error type.
import { useOnramp } from '@stripe/stripe-react-native'; function AttachKYCComponent() { const { attachKycInfo } = useOnramp(); const handleAttachKycInfo = useCallback(async () => { const kycInfo = { firstName: "FirstName", lastName:"LastName", idNumber: '000000000', dateOfBirth: { day: 1, month: 1, year: 1990, }, address: { line1: '123 Main St', line2: 'Apt 4B', city: 'San Francisco', state: 'CA', postalCode: '94111', country: 'US', }, }; const result = await attachKycInfo(kycInfo) if (result?.error) { // KYC information failed to attach. } else { // KYC information attached successfully. } }, []); return <Button title="Attach KYC" onPress={handleAttachKycInfo} />; }
Step 3: Verify KYC (if needed) Client-side
When a user already has KYC information, use presentKycInfoVerification so the SDK can present a screen with the user’s existing KYC information for verification. Currently, we only support users updating their address. See the VerifyKycResult for the return type and the OnrampError for the error type.
import { useOnramp } from '@stripe/stripe-react-native'; function VerifyKYCComponent() { const { presentKycInfoVerification } = useOnramp(); const handlePresentKycVerification = useCallback( async (updatedAddress: Address | null) => { const result = await presentKycInfoVerification(updatedAddress); if (result?.error) { if (result.error.code === 'Canceled') { // KYC Verification was canceled by the user. } else { // An error occurred during verification. } } else if (result?.status === 'Confirmed') { // KYC information was verified by the user. } else if (result?.status === 'UpdateAddress') { // User indicated that their address needs to be updated. } else { // KYC Verification was canceled by the user. } }, []); return <Button title="Verify KYC" onPress={handlePresentKycVerification} />; }
Step 4: Verify identity (if needed) Client-side
Some users must verify their identity before continuing with checkout. When required, use the verifyIdentity method. It presents a Stripe-hosted flow where the user uploads an identity document and a selfie.
Verification is asynchronous. After the user completes the flow, your backend can call the Retrieve a CryptoCustomer API and inspect the verifications array to see the results.
import { useOnramp } from '@stripe/stripe-react-native'; function VerifyIdentityComponent() { const { verifyIdentity } = useOnramp(); const handleVerifyIdentity = useCallback(async () => { const result = await verifyIdentity(); if (result?.error) { if (result.error.code === 'Canceled') { // Identity verification was cancelled. } else { // Error occurred during verification. } } else { // Identity verification was completed. } }, []); return <Button title="Verify Identity" onPress={handleVerifyIdentity} />; }
Payment
Step 1: Register a crypto wallet Client-side Server-side
A ConsumerWallet must be registered before you can create a PaymentToken. This validates that the address is valid for the given network. Your backend can call the List ConsumerWallets API to see whether the user already has wallets on file.
If the list is empty or the user wants to add another address, have the client call registerWalletAddress with the user’s chosen address and network. Replace the address and network with user-provided values. A previously registered wallet can be reused in future sessions.
Step 2: Collect a payment method Client-side Server-side
A payment method must first be collected before a transaction can occur. Your backend can call the List PaymentTokens API to see which payment methods the user already has. If the list is empty or the user wants to use a different method, have the client call collectPaymentMethod.
Card, Bank Account, and Apple Pay are supported. For Card and Bank Account, collectPaymentMethod presents Stripe’s wallet user interface, which lists existing stored payment methods, allows the user to add new ones, and select one. Upon successful payment method selection, it returns an instance of CollectPaymentMethodResult, which includes a displayData property (icon, label, sublabel) that you can use in your UI to show the selected payment method.
To collect Apple Pay, first check isPlatformPaySupported in useStripe(). See Apple Pay on React Native. If the user chooses Apple Pay, pass an instance of PlatformPay.PaymentMethodParams into collectPaymentMethod.
Step 3: Create a payment token Client-side
Create a PaymentToken by calling createCryptoPaymentToken. Use the returned token when creating the CryptoOnrampSession.
import { useOnramp } from '@stripe/stripe-react-native'; function CreatePaymentTokenComponent() { const { createCryptoPaymentToken } = useOnramp(); const handleCreateCryptoPaymentToken = useCallback(async () => { const result = await createCryptoPaymentToken(); if (result?.error) { // An error occurred while creating a crypto payment token. } else { // Use result.cryptoPaymentToken in your payment session. } }, []); return <Button title="Create Payment Token" onPress={handleCreateCryptoPaymentToken} />; }
Step 4: Create a crypto onramp session Server-side
From your UI, determine the amount, source currency (for example, usd), destination currency (for example, usdc), and network. Your backend calls the Create a CryptoOnrampSession API to create a CryptoOnrampSession. The Stripe React Native SDK doesn’t provide APIs for creating a crypto onramp session. It happens on your backend. The example below shows how a client application might call your backend. Adapt it to your use case.
When the user is paying with ACH, you can optionally pass settlement_ on create session (private feature; contact us if you need this).
Step 5: Perform checkout Client-side Server-side
Call performCheckout to run the checkout flow for a crypto onramp session. It presents a UI for any required actions such as 3DS.
You must implement the client-side callback fetchClientSecretFromBackend, which the SDK invokes to retrieve the checkout client secret. Have it call your back end, which calls the onramp session checkout endpoint with the session ID. The response includes the client_, which your callback can then return to the SDK.
For ACH, the API may indicate that mandate_ is missing. Collect acceptance and send it on a later checkout call if required.
When the API returns 200 or 202 but the purchase isn’t done, the response body includes the CryptoOnrampSession object with transaction_ set. Use that value to decide the next step:
| last_error | Description | How to handle |
|---|---|---|
action_ | User must complete a payment step (for example, 3D Secure). | Run the SDK’s 3DS handling. After the user completes the step, call checkout again. |
missing_ | KYC verification is required. | Have the user complete KYC in the SDK (for example, attachKycInfo). Then call checkout again. |
missing_ | Identity document verification is required. | Have the user complete verification in the SDK (for example, verifyIdentity). Then call checkout again. |
charged_ | Quote expired. | Call Refresh a Quote API, then call checkout again. |
transaction_ | User’s limit exceeded. | Display an error message. |
location_ | User’s location isn’t supported. | Show that the service isn’t available in their region. |
transaction_ | Generic failure. | Display a generic error message. |
missing_ | The wallet address doesn’t exist for the current user. | Have the user register the wallet, then call checkout again. |
Main objects
CryptoCustomer
A Crypto Customer represents a Link user in the context of your platform’s crypto onramp. It ties together user’s KYC and verification state, their consumer wallets, and their payment tokens. You use customerId in all onramp API calls to check what’s missing (for example, KYC or identity verification), to list wallets and payment tokens, and to create crypto onramp sessions.
| Attribute | Type | Description |
|---|---|---|
id | string | Unique identifier for the object (for example, crc_). |
provided_ | array of strings | The set of KYC fields. See KycField. |
verifications | array of Verification | List of verifications and their outcome. See Verification. |
KycField
| Value | Description |
|---|---|
first_ | Customer’s first name. |
last_ | Customer’s last name. |
id_ | Type of ID document (for example, passport or driver’s license). |
id_ | ID document number. |
dob | Date of birth. |
address_ | Address line 1. |
address_ | Address line 2. |
address_ | City. |
address_ | State or region. |
address_ | Postal or ZIP code. |
address_ | Country of address. |
birth_ | Country of birth. |
birth_ | City of birth. |
nationalities | Nationality or nationalities. |
id_ | Identity document (for example, image or verification reference). |
selfie | Selfie image for verification. |
Verification
| Attribute | Type | Description |
|---|---|---|
name | string (enum) | VerificationType |
status | string (enum) | VerificationStatus |
errors | array of strings (enum) | VerificationError |
VerificationType
| Value | Description |
|---|---|
kyc_ | Verification that the customer has submitted required KYC information. |
id_ | Verification that the customer has submitted and verified their identity documents. |
VerificationStatus
| Value | Description |
|---|---|
not_ | Customer has not provided the info. |
pending | Verification has been submitted and is processing. |
rejected | Verification failed. See VerificationError for details. |
verified | Verification complete. The customer may continue. |
VerificationError
| Value | Description |
|---|---|
id_ | Identity document verification didn’t succeed (for example, document was rejected or couldn’t be verified). |
user_ | The user has reached the maximum number of verification attempts and must wait or contact support before trying again. |
CryptoOnrampSession
See Stripe API docs for the object’s attributes.
PaymentToken
A Payment Token is a read-only representation of a payment method that belongs to a CryptoCustomer and applies only to crypto onramp.
| Attribute | Type | Description |
|---|---|---|
id | string | Unique identifier for the payment token (for example, cpt_). |
type | string (enum) | PaymentMethodType |
card | object (optional) | Card |
us_ | object (optional) | UsBankAccount |
PaymentMethodType
| Value | Description |
|---|---|
card | Card payment method. |
us_ | US bank account payment method. |
Card
| Attribute | Type | Description |
|---|---|---|
brand | string (optional) | Card brand (for example, amex, mastercard, visa). |
last4 | string (optional) | Last four digits of the card. |
exp_ | integer (optional) | Two-digit expiration month (1–12). |
exp_ | integer (optional) | Four-digit expiration year. |
funding | string | Card funding type (for example, credit, debit, prepaid, unknown). |
wallet | object (optional) | Wallet |
Wallet
| Attribute | Type | Description |
|---|---|---|
type | string (enum) | WalletType |
WalletType
| Value | Description |
|---|---|
apple_ | Apple Pay wallet. |
UsBankAccount
| Attribute | Type | Description |
|---|---|---|
account_ | string (enum, optional) | AccountType |
last4 | string (optional) | Last four digits of the bank account number. |
bank_ | string (optional) | Name of the bank. |
AccountType
| Value | Description |
|---|---|
checking | Checking account. |
savings | Savings account. |
ConsumerWallet
A Consumer Wallet is a cryptocurrency wallet address linked to a CryptoCustomer. It’s the destination where purchased crypto is sent. The user registers wallet addresses during account setup.
| Attribute | Type | Description |
|---|---|---|
id | string | Unique identifier for the consumer wallet. |
livemode | boolean | Whether this wallet is in live mode (true) or test mode (false). |
network | string (enum) | Network |
wallet_ | string | The blockchain address (for example, the destination address for crypto delivery). |
Network
| Value | Description |
|---|---|
bitcoin | Bitcoin network. |
ethereum | Ethereum network. |
solana | Solana network. |
polygon | Polygon network. |
base | Base network. |
avalanche | Avalanche network. |
stellar | Stellar network. |
aptos | Aptos network. |
optimism | Optimism network. |
worldchain | Worldchain network. |
xrpl | XRP Ledger network. |
API specs
Retrieve a CryptoCustomer
Retrieves the details of a CryptoCustomer. Use the response to check the customer’s KYC and verification status so you can prompt the user to complete setup (for example, KYC or identity verification) before creating an onramp session.
curl https://api.stripe.com/v1/crypto/customers/{customerId} \ -H "Authorization: Bearer $STRIPE_SECRET_KEY" \ -H "Stripe-OAuth-Token: $ACCESS_TOKEN"
// Response { "id": "crc_XXXX", "object": "object.public_crypto_customer", "provided_fields": ["first_name", "last_name"], "verifications": [ { "name": "kyc_verified", "status": "verified", "errors": [] }, { "name": "id_document_verified", "status": "rejected", "errors": ["id_document_verification_failed"] } ] }
Parameters
| Parameter | Type | Description |
|---|---|---|
id | string (required) | The crypto customer ID (for example, crc_). |
Returns
Returns a CryptoCustomer object.
Errors
| HTTP status | Cause |
|---|---|
| 400 | Missing OAuth token. The request didn’t include the Stripe-OAuth-Token header. |
| 403 | Invalid or insufficient OAuth token. The token is missing, invalid, expired, has wrong scopes (for example, missing kyc.status:read), or wrong livemode. |
| 403 | Customer belongs to another user. The CryptoCustomer exists but is tied to a different Link consumer than the one identified by the OAuth token. |
| 404 | Customer not found. The :id isn’t a valid crypto customer ID. |
| 404 | Headless onramp not enabled. The request is from a business that doesn’t have the headless onramp gate/feature enabled. |
List ConsumerWallets
Returns a list of ConsumerWallet for a CryptoCustomer. Use this to see which wallet addresses are on file and whether the user needs to register a new one. Each wallet has an id, network, and address.
curl "https://api.stripe.com/v1/crypto/customers/{customerId}/crypto_consumer_wallets" \ -H "Authorization: Bearer $STRIPE_SECRET_KEY" \ -H "Stripe-OAuth-Token: $ACCESS_TOKEN"
// Response { "data": [ { "id": "ccw_xxx", "object": "crypto.consumer_wallet", "livemode": false, "network": "ethereum", "wallet_address": "0x1234567890abcdef1234567890abcdef12345678" } ], "has_more": false }
Parameters
| Parameter | Type | Description |
|---|---|---|
id | string (required) | The crypto customer id (for example, crc_). |
limit | integer (optional) | Maximum number of wallets to return. Default is 10; maximum is 20. |
starting_ | string (optional) | A wallet id. Return wallets after this id (for forward pagination). |
ending_ | string (optional) | A wallet id. Return wallets before this id (for backward pagination). |
Returns
| Field | Type | Description |
|---|---|---|
data | array | List of ConsumerWallet objects. |
has_ | boolean | Whether there are more wallets after this page. |
url | string | The URL of this list. |
Errors
| HTTP status | Cause |
|---|---|
| 400 | Missing required param. |
| 403 | Invalid request: invalid or expired OAuth token, wrong scopes, livemode mismatch, or crypto customer belongs to a different consumer. |
| 404 | No such cryptocustomer: customer id invalid or not found. |
| 404 | Headless onramp not enabled for this business. |
List PaymentTokens
Returns a list of PaymentToken for a CryptoCustomer. Use this to see which saved payment methods the user has before asking them to add one.
curl https://api.stripe.com/v1/crypto/customers/{customerId}/payment_tokens \ -H "Authorization: Bearer $STRIPE_SECRET_KEY" \ -H "Stripe-OAuth-Token: $ACCESS_TOKEN"
// Response { "url": "/v1/crypto/customers/crc_xxx/payment_tokens", "data": [ { "id": "cpt_xxx", "object": "crypto.payment_token", "type": "card", "card": { "brand": "visa", "last4": "4242", "exp_month": 12, "exp_year": 2028, "funding": "credit" } } ], "has_more": false }
Parameters
| Parameter | Type | Description |
|---|---|---|
id | string (required) | The crypto customer id (for example, crc_). Must belong to the consumer identified by the OAuth token. |
limit | integer (optional) | Maximum number of payment tokens to return. Default is 10. |
starting_ | string (optional) | A payment token id. Return tokens after this id (for forward pagination). |
ending_ | string (optional) | A payment token id. Return tokens before this id (for backward pagination). |
Returns
| Field | Type | Description |
|---|---|---|
data | array | List of PaymentToken objects. |
has_ | boolean | Whether there are more payment tokens after this page. |
url | string | The URL of this list. |
Errors
| HTTP status | Cause |
|---|---|
| 400 | Missing required param: HTTP_HEADER[Stripe-OAuth-Token]. Send the Stripe-OAuth-Token header with a valid OAuth access token. |
| 403 | Invalid request: invalid or expired OAuth token, missing crypto:ramp scope, livemode mismatch, or crypto customer belongs to a different consumer. |
| 404 | No such crypto customer: customer id invalid or not found. |
| 404 | Headless onramp not enabled for this business. |
Checkout
Completes a CryptoOnrampSession by confirming the payment and executing the quote so crypto is delivered to the customer’s wallet. Call this after creating an onramp session. If the response indicates more steps (for example, 3DS), have the user complete them in the SDK and call checkout again.
curl -X POST https://api.stripe.com/v1/crypto/onramp_sessions/{sessionId}/checkout \ -H "Authorization: Bearer $STRIPE_SECRET_KEY" \ -H "Stripe-OAuth-Token: $ACCESS_TOKEN"
// Response { "id": "cos_xxx", "object": "crypto.onramp_session", "status": "fulfillment_complete", "transaction_details": { "wallet_address": "0xx", "source_amount": "100.00", "source_currency": "usd", "destination_amount": "0.05", "destination_currency": "eth", "destination_network": "ethereum", "quote_expiration": 1756238966, "transaction_id": "0xx" } }
Parameters
| Parameter | Type | Description |
|---|---|---|
id | string (required) | The onramp session id (for example, cos_) to checkout. The session must have been created with ui_ and be in a state that allows checkout. |
mandate_ | object (optional) | Details for creating a mandate when required (for example, for certain payment method types or regions). Include when the API or payment method requires it. |
Returns
Returns the updated CryptoOnrampSession object.
Errors
| HTTP status | Cause |
|---|---|
| 400 | Invalid request: missing or invalid parameters, unsupported currency or network, or invalid or missing OAuth token. |
| 400 | MissingKYC: user has not completed KYC. |
| 400 | InvalidWallet: wallet not attached to this user. |
| 400 | InvalidPaymentMethod: payment method not attached to this user. |
| 400 | Unauthorized: OAuth token invalid or missing. |
| 404 | Session not found or not owned by this business. |
Refresh a Quote
Refreshes the quote for a CryptoOnrampSession. Use this when checkout returns charged_ so you can get a new quote and call checkout again on the same session instead of creating a new one.
curl -X POST https://api.stripe.com/v1/crypto/onramp_sessions/{sessionId}/quote \ -H "Authorization: Bearer $STRIPE_SECRET_KEY" \ -H "Stripe-OAuth-Token: $ACCESS_TOKEN"
// Response { "id": "cos_XXXX", "object": "crypto.onramp_session", "status": "requires_payment", "transaction_details": { "wallet_address": "0x1234567890abcdef1234567890abcdef12345678", "source_amount": "100.00", "source_currency": "usd", "destination_amount": "0.05", "destination_currency": "eth", "destination_network": "ethereum", "quote_expiration": 1756238966 } }
Parameters
| Parameter | Type | Description |
|---|---|---|
id | string (required) | The crypto onramp session ID (for example, cos_). Provided in the URL path. |
Returns
Returns a CryptoOnrampSession object with an updated quote.
Errors
| HTTP status | Cause |
|---|---|
| 403 | Quote locked. The session quote is in a locked state. Create a new session and call checkout on it instead. |
| 403 | Internal error. The session has no consumer account, or the liquidity provider account couldn’t be found. |
| 404 | Session not found. The id isn’t a valid crypto onramp session ID or the session doesn’t exist. |
| 404 | Headless onramp not enabled. The request is from a business that doesn’t have the headless onramp gate/feature enabled. |
| 499 | Partner exchange error. The quote provider returned an error or timeout. |
Create a LinkAuthIntent
Creates a LinkAuthIntent to start a Log in with Link flow. Send the OAuth client id and scopes you need. The API returns an intent id and expiration.
This API is still in private preview. Sign up to get access to additional API info.
curl -X POST https://login.link.com/v1/link_auth_intent \ -H "Authorization: Bearer $STRIPE_SECRET_KEY" \ -d email=user@example.com \ -d oauth_client_id=$OAUTH_CLIENT_ID \ -d oauth_scopes=kyc.status:read,crypto:ramp
// Response { "id": "lai_xxxx", "expires_at": 1756238966 }
Parameters
| Parameter | Type | Description |
|---|---|---|
email | string (required) | The user’s email for looking up an existing Link consumer. Provide either email or hashed_, not both. |
hashed_ | string (required*) | A SHA256 hash of the plain text email for privacy-sensitive flows. Provide either email or hashed_, not both. |
oauth_ | string (required) | Your OAuth client id (for example, from Link). Identifies your application in the OAuth flow. |
oauth_ | string (required) | Comma-separated list of OAuth scopes (for example, kyc.). Defines the permissions you’re requesting. |
data_ | string (optional) | When set, the recipient business ID for data-sharing (for example, crypto onramp). Must be a valid business ID enabled to receive OAuth tokens. |
Returns
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for the LinkAuthIntent (for example, lai_). |
expires_ | integer | Unix timestamp when the intent expires. |
Errors
| HTTP status | Cause |
|---|---|
| 400 | Missing or invalid request body. |
| 403 | CreateLinkAuthIntent not enabled for business or invalid or missing API key. |
| 404 | OAuth client not found for authIntentId or no active Link consumer for the provided email. |
| 409 | Link consumer previously revoked connection with this partner. |
Retrieve Access Tokens
Exchanges a consented LinkAuthIntent for an OAuth access token. Call this after the user has completed authorization. Use the access token (for example, in the Stripe-OAuth-Token header) on subsequent onramp API requests for that user.
This API is still in private preview. Sign up to get access to additional API info.
curl -X POST https://login.link.com/v1/link_auth_intent/{authIntentId}/tokens \ -H "Authorization: Bearer $STRIPE_SECRET_KEY"
// Response { "access_token": "liwltoken_xxx", "expires_in": 3600, "token_type": "Bearer", "refresh": { "refresh_token": "liwlrefresh_xxx", "expires_in": 7776000 } }
Parameters
| Parameter | Type | Description |
|---|---|---|
id | string (required) | The Link Auth Intent id (for example, lai_). |
Returns
| Field | Type | Description |
|---|---|---|
access_ | string | OAuth access token. Send it on subsequent API requests for this user (for example, in the Stripe-OAuth-Token header). |
token_ | string | Token type. Always Bearer. |
expires_ | integer | Seconds until the access token expires. |
refresh | object (optional) | Present when a refresh token was issued. Use it to obtain a new access token when the current one expires. See Refresh an Access Token. |
refresh. | string | OAuth refresh token. Store it securely and use it to obtain new access tokens. See Refresh an Access Token. |
refresh. | integer | Seconds until the refresh token expires. |
Errors
| HTTP status | Cause |
|---|---|
| 403 | Feature not available. |
| 403 | LinkAuthIntent has not been consented. |
| 403 | Invalid or missing API key. |
| 404 | LinkAuthIntent not found (invalid id or intent belongs to another business). |
Refresh an Access Token
Exchanges a refresh token for a new access token. When your access token expires, use the refresh token you received from Retrieve Access Tokens to obtain a new access token without requiring the user to re-authorize.
This API is still in private preview. Sign up to get access to additional API info.
curl -X POST https://login.link.com/auth/token \ -H "Authorization: Bearer $STRIPE_SECRET_KEY" \ -d "grant_type=refresh_token" \ -d "refresh_token=$REFRESH_TOKEN" \ -d "client_id=$OAUTH_CLIENT_ID" \ -d "client_secret=$OAUTH_CLIENT_SECRET"
// Response { "token_type": "Bearer", "access_token": "liwltoken_xxx", "expires_in": 3600, "refresh": { "refresh_token": "liwlrefresh_xxx", "expires_in": 7776000 }, "scope": "kyc.status:read crypto:ramp" }
Parameters
| Parameter | Type | Description |
|---|---|---|
grant_ | string (required) | Must be refresh_. |
refresh_ | string (required) | The refresh token previously obtained from Retrieve Access Tokens. |
client_ | string (required) | Your OAuth client ID provided by Link. |
client_ | string (required) | Your OAuth client secret provided by Link. |
Returns
| Field | Type | Description |
|---|---|---|
token_ | string | Token type. Always Bearer. |
access_ | string | OAuth access token. Expires in 1 hour. Send it on subsequent API requests (for example, in the Stripe-OAuth-Token header). |
expires_ | integer | TTL in seconds (3600, that is, 1 hour). |
refresh | object (optional) | Present when a new refresh token was issued. A new refresh token is returned each time you use the old one; store it for future refresh requests. |
refresh. | string | OAuth refresh token. Store it securely for obtaining new access tokens when the current one expires. |
refresh. | integer | Seconds until the refresh token expires. |
scope | string | The OAuth scopes granted. |
React Native SDK
| SDK method | Presents UI? | What the user sees |
|---|---|---|
authorize(authIntentId) | Yes | Link consent screen (or consent inline on OTP screen). |
attachKycInfo | Optional | You can collect KYC in your own UI and pass data in. |
presentKycInfoVerification | Yes | Stripe screen showing existing KYC for the user to verify. |
verifyIdentity | Yes | Stripe-hosted flow (document + selfie). |
collectPaymentMethod (Card / BankAccount) | Yes | Stripe wallet UI: list saved methods, add new, choose one. |
performCheckout | Maybe | Only when needed (for example, 3DS). |
registerWalletAddress | No | No UI. You pass address and network. |