Agentic Purchases
Allow an agent to make a purchase on behalf of a consumer using Basis Theory's secure tokenization and AI platformThis guide walks you through implementing agentic purchases, where agents can securely make purchases on behalf of consumers using Basis Theory's tokenization platform and AI services.
Prerequisites
Platforms
Basis Theory Vault
Our core tokenization and payments platform, used to store and use credit card data with any service without introducing PCI compliance.
Basis Theory AI
Our BTAI offering enables the conversion of a Basis Theory Token from our core platform into an Agentic Credntial (e.g. Visa Intelligent Commerce or Mastercard Agent Pay product).
Connect Basis Theory and Basis Theory AI
Reach out to the Basis Theory team to provision your Basis Theory AI project and connect your Basis Theory Tenant.
1. Collecting Card Data with Elements
Use Basis Theory Elements to securely store credit card data and convert it into a Basis Theory Token. This approach replaces processor-specific iFrames with vault-agnostic secure elements, ensuring your systems never come in contact with sensitive card data while maintaining PCI compliance.
Public Application
You will need a Public Application for your frontend. Click here to create one using the Basis Theory Customer Portal.
This will create an application with the following Access Controls:
- Permissions:
token:create
Configure Elements SDK
Basis Theory Elements are available for the following technologies. Click below for detailed instructions on how to install and configure them:
Add Card Elements to your Page
Once installed and configured, add the Card Elements to your application. This will enable your users to type in their card data in your form, while ensuring your systems never come in contact with the data.
- JavaScript
- React
- React Native
<div id="cardNumber"></div>
<div style="display: flex;">
<div id="cardExpirationDate" style="width: 100%;"></div>
<div id="cardVerificationCode" style="width: 100%;"></div>
</div>
import { basistheory } from '@basis-theory/web-elements';
let bt;
let cardNumberElement;
let cardExpirationDateElement;
let cardVerificationCodeElement;
async function init() {
bt = await basistheory('<API_KEY>');
// Creates Elements instances
cardNumberElement = bt.createElement('cardNumber', {
targetId: 'myCardNumber' // (custom) used for tracking validation errors
});
cardExpirationDateElement = bt.createElement('cardExpirationDate', {
targetId: 'myCardExpiration'
});
cardVerificationCodeElement = bt.createElement('cardVerificationCode', {
targetId: 'myCardVerification'
});
// Mounts Elements in the DOM in parallel
await Promise.all([
cardNumberElement.mount('#cardNumber'),
cardExpirationDateElement.mount('#cardExpirationDate'),
cardVerificationCodeElement.mount('#cardVerificationCode'),
]);
// Binds card brand to verification code element
cardNumberElement.on('change', ({ cardBrand }) => {
cardVerificationCodeElement.update({ cardBrand });
});
}
init();
import React, { useRef, useState } from 'react';
import {
BasisTheoryProvider,
CardNumberElement,
CardExpirationDateElement,
CardVerificationCodeElement,
useBasisTheory,
} from '@basis-theory/react-elements';
export default function App() {
const { bt } = useBasisTheory('<PUBLIC_API_KEY>');
// Refs to get access to the Elements instance
const cardNumberRef = useRef(null);
const cardExpirationRef = useRef(null);
const cardVerificationRef = useRef(null);
// stores the current card brand in state, to pass to CardVerificationCodeElement
const [cardBrand, setCardBrand] = useState();
return (
<BasisTheoryProvider bt={bt}>
<CardNumberElement
id="myCardNumber"
ref={cardNumberRef}
onChange={({ cardBrand }) => setCardBrand(cardBrand)}
/>
<div style={{ display: 'flex' }}>
<div style={{ width: "100%" }}>
<CardExpirationDateElement
id="myCardExpiration"
ref={cardExpirationRef}
/>
</div>
<div style={{ width: "100%" }}>
<CardVerificationCodeElement
id="myCardVerification"
ref={cardVerificationRef}
cardBrand={cardBrand}
/>
</div>
</div>
</BasisTheoryProvider>
);
}
import React, { useRef } from "react";
import { Button, SafeAreaView, ScrollView, StatusBar, StyleSheet, View } from "react-native";
import type { BTRef } from "@basis-theory/react-native-elements";
import {
CardNumberElement,
CardExpirationDateElement,
CardVerificationCodeElement,
useBasisTheory
} from "@basis-theory/react-native-elements";
const App = (): JSX.Element => {
const { bt } = useBasisTheory('<PUBLIC_API_KEY>');
// Refs to get access to the Elements instance
const cardNumberRef = useRef<BTRef>(null);
const cardExpirationDateRef = useRef<BTRef>(null);
const cardVerificationCodeRef = useRef<BTRef>(null);
return (
<SafeAreaView>
<StatusBar />
<ScrollView contentInsetAdjustmentBehavior="automatic">
<View style={styles.viewContainer}>
<CardNumberElement btRef={cardNumberRef} placeholder="Card Number" style={styles.elements} />
<CardExpirationDateElement btRef={cardExpirationDateRef} placeholder="Card Expiration Date" style={styles.elements} />
<CardVerificationCodeElement btRef={cardVerificationCodeRef} placeholder="CVC" style={styles.elements} />
</View>
</ScrollView>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
cardNumber: {
backgroundColor: "#eeeeee",
borderColor: "blue",
borderWidth: 2,
color: "purple",
height: 40,
margin: 12,
padding: 10,
},
viewContainer: {
display: "flex",
flexDirection: "column",
marginTop: 32,
},
});
export default App;
Tokenization
Now that you are securely capturing the cardholder data in your user-facing application(s), it is time to create a Token directly. We'll use bt.tokens.create
to immediately tokenize the card data and store it securely in your Basis Theory vault.
Add a submit function along with a button to trigger it:
- JavaScript
- React
- React Native
<div id="cardNumber"></div>
<div style="display: flex;">
<div id="cardExpirationDate" style="width: 100%;"></div>
<div id="cardVerificationCode" style="width: 100%;"></div>
</div>
<button id="submit">Submit</button>
import { BasisTheory } from '@basis-theory/basis-theory-js';
let bt;
let cardNumberElement;
let cardExpirationDateElement;
let cardVerificationCodeElement;
async function init () {
...
document.getElementById("submit").addEventListener("click", submit);
}
async function submit () {
try {
const token = await bt.tokens.create({
type: 'card',
data: {
number: cardNumberElement,
expiration_month: cardExpirationDateElement.month(),
expiration_year: cardExpirationDateElement.year(),
cvc: cardVerificationCodeElement,
}
});
// TODO post the token object to your backend
console.log('Token created:', token.id);
} catch (error) {
console.error(error);
}
}
init();
import React, { useRef, useState } from 'react';
import {
BasisTheoryProvider,
CardNumberElement,
CardExpirationDateElement,
CardVerificationCodeElement,
useBasisTheory,
} from '@basis-theory/react-elements';
export default function App() {
const { bt } = useBasisTheory('<PUBLIC_API_KEY>');
// Refs to get access to the Elements instance
const cardNumberRef = useRef(null);
const cardExpirationRef = useRef(null);
const cardVerificationRef = useRef(null);
// stores the current card brand in state, to pass to CardVerificationCodeElement
const [cardBrand, setCardBrand] = useState();
const submit = async () => {
try {
const token = await bt?.tokens.create({
type: 'card',
data: {
number: cardNumberRef.current,
expiration_month: cardExpirationRef.current.month(),
expiration_year: cardExpirationRef.current.year(),
cvc: cardVerificationRef.current,
}
});
// TODO post the token object to your backend
console.log('Token created:', token.id);
} catch (error) {
console.error(error);
}
}
return (
<BasisTheoryProvider bt={bt}>
...
<button onClick={submit}>Submit</button>
</BasisTheoryProvider>
);
}
import React, { useRef } from "react";
import { Button, SafeAreaView, ScrollView, StatusBar, StyleSheet, View } from "react-native";
import type { BTRef } from "@basis-theory/react-native-elements";
import {
CardNumberElement,
CardExpirationDateElement,
CardVerificationCodeElement,
useBasisTheory
} from "@basis-theory/react-native-elements";
const App = (): JSX.Element => {
const { bt } = useBasisTheory('<PUBLIC_API_KEY>');
// Refs to get access to the Elements instance
const cardNumberRef = useRef<BTRef>(null);
const cardExpirationDateRef = useRef<BTRef>(null);
const cardVerificationCodeRef = useRef<BTRef>(null);
const submit = async () => {
try {
const token = await bt?.tokens.create({
type: 'card',
data: {
number: cardNumberRef.current,
expiration_month: cardExpirationRef.current.month(),
expiration_year: cardExpirationRef.current.year(),
cvc: cardVerificationRef.current,
}
});
// TODO post the token object to your backend
console.log('Token created:', token.id);
} catch (error) {
console.error(error);
}
}
return (
<SafeAreaView>
<StatusBar />
<ScrollView contentInsetAdjustmentBehavior="automatic">
<View style={styles.viewContainer}>
<CardNumberElement btRef={cardNumberRef} placeholder="Card Number" style={styles.elements} />
<CardExpirationDateElement btRef={cardExpirationDateRef} placeholder="Card Expiration Date" style={styles.elements} />
<CardVerificationCodeElement btRef={cardVerificationCodeRef} placeholder="CVC" style={styles.elements} />
</View>
<Button onPress={submit} title="Submit" />
</ScrollView>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
cardNumber: {
backgroundColor: "#eeeeee",
borderColor: "blue",
borderWidth: 2,
color: "purple",
height: 40,
margin: 12,
padding: 10,
},
viewContainer: {
display: "flex",
flexDirection: "column",
marginTop: 32,
},
});
export default App;
The created Token is a long-lived tokenized card
object which carries only non-sensitive information. You can either post the complete token object to your backend for processing or use the token ID to reference it in subsequent API calls.
2. Using Token with Basis Theory AI
The next steps enable the Basis Theory AI platform to convert a Basis Theory Token into a Visa Intelligent Commerce or Mastercard Agent Pay credential.
Create a JWT
You can create a JWT using the JavaScript examples in the Authentication section of our AI documentation.
Create Payment Method
Once a token has been created in Basis Theory, it can be utilized to create a new Payment Method within the Basis Theory AI platform. This call can happen either from your Frontend or your Backend application:
curl -X POST \
https://api.basistheory.ai/projects/:projectId/payment-methods \
-H 'Authorization: Bearer YOUR_JWT_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"entityId": "user-12345",
"tokenId": "<Basis Theory Token Id>"
}'
Parameters:
entityId
- A platform's ability to specify the user (or their customer) who owns this Payment Method. This can be used to scope an authentication JWT to restrict access to only a specificentityId
's Payment Methods or Purchase Intents - giving granular control over access.tokenId
- The Id returned from your Elements integration.
Create a Purchase Intent
Once a payment method is created, it can be used to make a purchase intent to generate a Visa Intelligent Commerce or Mastercard Agent Pay virtual card credential. Purchase Intents go through a short lifecycle of statuses to allow this access.
curl -X POST \
https://api.basistheory.ai/projects/:projectId/purchase-intents \
-H 'Authorization: Bearer YOUR_JWT_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"entityId": "user-12345",
"credentialType": "virtual-card",
"paymentMethodId": "26859380-7829-4ee1-8a0a-38927881e7ef",
"mandates": [
{
"type": "maxAmount",
"value": "100",
"details": {
"currency": "840"
}
},
{
"type": "consumer",
"value": "3d50aca6-9d1e-4459-8254-4171a92f5bd0",
"details": {
"email": "customer@email.com"
}
}
]
}'
Purchase Intent Statuses:
verify
- Initial status for Intents that require verification before credentials can be accessedactive
- Status after successful verification or if no verification is requiredexpired
- Status when the purchase intent has passed its expiration time
Verify Purchase Intent
Once a purchase intent has been created, most of the time it must be verified on the front end to confirm that the user owns the payment method and agree with the purchase. Basis Theory handles all of the complexity of this verification.
The React SDK can be found here, with detailed guides on how to utilize it. We plan to release a non-React version soon.
import { useBasisTheory } from '@basis-theory-ai/react';
function VerificationComponent() {
const { verifyPurchaseIntent } = useBasisTheory();
const handleVerify = async () => {
try {
const result = await verifyPurchaseIntent(projectId, purchaseIntentId);
console.log('Verification result:', result);
} catch (error) {
console.error('Verification failed:', error);
}
};
return (
<button onClick={handleVerify}>
Verify Purchase Intent
</button>
);
}
Verification Steps handled by Basis Theory
The following flows are handled and coordinated regardless of the Network by Basis Theory. We will optimize to make this as seamless as possible for future requests by the same consumer.
-
Authentication of the owner
- Basis Theory connects with the Networks to create a 2FA flow to verify the consumer. Once the consumer has completed this on their browser/device, they will typically not need to repeat it. We use a unique fingerprint for that device on subsequent authentications.
-
Authorization from the user
- For some purchase intents, Basis Theory will set up a passkey for future requests, which will act as a fast path for consumers to approve purchases in the future.
Retrieve Virtual Card
Once a Purchase Intent has been verified, simply retrieving it from your backend will return the final Virtual Card, which is ready to be injected into any payment flow your Agent needs.
curl -X GET \
https://api.basistheory.ai/projects/:projectId/purchase-intents/:id \
-H 'Authorization: Bearer YOUR_JWT_TOKEN'
FAQ
Can I also process my own payments with the cards collected?
Yes - to process payments with your tokenized card data, refer to these comprehensive guides:
- Charge a Card - Complete guide for processing payments with various processors including Stripe, Adyen, and others
- Verify a Card - Validate cards by performing $0 authorizations before processing actual payments
Can I integrate with other agentic platforms with cards collected?
Once you have created a Basis Theory Token using Elements, you can use that token with any payment processor or agentic payments partner. The Basis Theory platform provides flexible options for integrating with various processors without being locked into any specific provider.
For custom processor integrations or agentic payments partners not covered in our standard guides, use the Basis Theory Proxy:
- Proxy API Reference - Complete API documentation for using the proxy to integrate with any HTTP-based payment service
- Invoke Proxy - Direct API calls for invoking proxy requests with your tokens