React Agentic
react-agentic
The Basis Theory @basis-theory/react-agentic package provides React components and hooks for building agentic commerce flows. It handles enrollment verification (OTP + passkey creation) and instruction approval (passkey authentication) with pre-built UI components.
The card network SDKs used for verification (Visa and Mastercard) require your application to be served over HTTPS, even in development. Verification will fail on plain HTTP.
For local development, we recommend Cloudflare Quick Tunnels to expose your local server over HTTPS with zero configuration:
# Install cloudflared
brew install cloudflared
# Start a quick tunnel pointing to your local dev server
cloudflared tunnel --url http://localhost:3000
This gives you a temporary https://*.trycloudflare.com URL you can use for testing.
Installation
- npm
- yarn
npm install @basis-theory/react-agentic
yarn add @basis-theory/react-agentic
If your project uses React v19 or Next.js 16+, add react and react-dom as peer dependencies and use pnpm overrides to resolve version conflicts:
{
"peerDependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"pnpm": {
"overrides": {
"react": "^19.0.0",
"react-dom": "^19.0.0"
}
}
}
React 16–18 work without modification.
Setup
Wrap your application with the BtAiProvider to authenticate requests using your Public API key:
import { BtAiProvider } from '@basis-theory/react-agentic';
function App() {
return (
<BtAiProvider apiKey="<PUBLIC_API_KEY>">
<YourApp />
</BtAiProvider>
);
}
BtAiProvider Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
apiKey | string | Yes | - | Basis Theory public API key |
environment | "production" | "test" | "sandbox" | No | "production" | Environment to use. test and sandbox use mock providers for Visa. |
agenticApiUrl | string | No | - | Override the agentic commerce API base URL |
passkeyTimeoutMs | number | No | 60000 | Timeout in milliseconds for Visa passkey authentication. Increase if users need more time to complete passkey prompts. |
sessionReadyTimeoutMs | number | No | 10000 | Timeout in milliseconds for Visa session readiness after refresh. |
onNewDevice | "decline" | "reenroll" | No | "reenroll" | Behavior when instruction verification detects the consumer is on a different device than the one used during enrollment. "decline" shows an error asking the user to return to the original device. "reenroll" prompts the user to re-enroll the card on the current device. |
theme | object | No | Dark theme | Partial theme overrides for the built-in verification modals. Deep-merged with the default dark theme — only override what you need. See Theming. |
In the test environment, the SDK uses a mock Visa SDK and validates real SDK configurations in the background, logging warnings if production setup has issues. This lets you develop locally without HTTPS while still catching configuration problems early.
Hooks
useAgentic
The useAgentic hook provides methods for enrollment verification, instruction approval, and SDK status. It must be used within a BtAiProvider.
import { useAgentic } from '@basis-theory/react-agentic';
function MyComponent() {
const {
ready,
verifyEnrollment,
verifyInstruction,
getStatus,
updateApiKey,
} = useAgentic();
// ...
}
Return Values
| Property | Type | Description |
|---|---|---|
ready | boolean | true when the SDK is initialized and ready for verification calls. On HTTPS, both Visa and Mastercard SDKs must be ready. On localhost, Mastercard-only is accepted since Visa requires HTTPS. |
verifyEnrollment | (enrollmentId: string) => Promise | Opens a modal for enrollment verification. Safe to call before ready — it waits automatically. |
verifyInstruction | (agentId: string, instructionId: string) => Promise | Opens a modal for instruction verification. Safe to call before ready — it waits automatically. |
getStatus | () => { visa: boolean, mastercard: boolean } | Returns per-network SDK readiness status. |
updateApiKey | (newApiKey: string) => void | Dynamically changes the API key, triggering re-initialization. |
apiKey | string | The current API key. |
environment | string | The current environment. |
verifyEnrollment
Renders a modal that guides the consumer through OTP verification and passkey creation to verify card enrollment.
const result = await verifyEnrollment(enrollmentId);
| Parameter | Type | Description |
|---|---|---|
enrollmentId | string | The enrollment ID to verify |
See the Agentic Credentials guide for a complete usage example.
If verification fails and the user closes the modal, the promise rejects with an error that includes error details (traceId, debug, code, status) when available:
try {
const result = await verifyEnrollment(enrollmentId);
} catch (error) {
console.error(error.message); // Human-readable error message
console.error(error.traceId); // BT-TRACE-ID for support
console.error(error.code); // Error code (e.g., "PROVIDER_VERIFICATION_FAILED")
}
verifyInstruction
Renders a modal for passkey authentication to approve a spending instruction.
const result = await verifyInstruction(agentId, instructionId);
| Parameter | Type | Description |
|---|---|---|
agentId | string | The agent ID |
instructionId | string | The instruction ID to verify |
See the Agentic Credentials guide for a complete usage example.
If verification fails and the user closes the modal, the rejected error includes the same error details as verifyEnrollment.
useEnrollmentVerification
Action-based hook for building a custom enrollment verification UI. Render your UI based on status and call the corresponding action to advance the flow. Use this instead of verifyEnrollment() when you need full control over the verification experience.
The hook handles orchestration for both card networks:
- Visa: OTP method selection → OTP input → passkey creation → completion
- Mastercard: popup-based authentication → completion
import { useEnrollmentVerification } from '@basis-theory/react-agentic';
function CustomEnrollmentUI({ enrollmentId }) {
const {
status,
error,
otpMethods,
start,
selectOtpMethod,
submitOtp,
confirmPasskey,
retry,
cancel,
} = useEnrollmentVerification({ enrollmentId });
// Render your own UI based on status
}
Input Props
| Prop | Type | Required | Description |
|---|---|---|---|
enrollmentId | string | Yes | The enrollment ID to verify |
Statuses
| Status | Description | Available Actions |
|---|---|---|
idle | Not started | start() |
loading | Async operation in progress | — |
otp_required | Show OTP method picker (otpMethods available) | selectOtpMethod(methodId) |
otp_sent | Show OTP code input | submitOtp(code) |
passkey_required | Visa passkey creation needed | confirmPasskey() |
popup_required | Mastercard popup needed | confirmPasskey(), retry() |
verified | Verification complete | — |
error | Something went wrong (error has message) | retry(), start() |
Return Values
| Property | Type | Description |
|---|---|---|
status | string | Current status (see table above) |
error | string | null | Error message when status is error |
errorDetails | object | null | Structured error info when status is error. See error details object. |
otpMethods | array | Available OTP delivery methods when status is otp_required |
start | () => Promise | Begin or restart the verification flow |
selectOtpMethod | (methodId: string) => Promise | Select an OTP delivery method |
submitOtp | (code: string) => Promise | Submit the OTP code |
confirmPasskey | () => Promise | Trigger passkey creation (Visa) or popup authentication (Mastercard) |
retry | () => Promise | Retry the current step |
cancel | () => void | Cancel and reset to idle |
How to Build Your Own UI
- Call
start()to kick off the enrollment verification flow. - Switch on
statusto render the appropriate UI for each step:loading— show a spinner or loading indicatorotp_required— render a list or buttons fromotpMethodsand callselectOtpMethod(methodId)on selectionotp_sent— render a code input field and callsubmitOtp(code)on submitpasskey_required— show a prompt explaining passkey creation and callconfirmPasskey()popup_required— show a prompt explaining Mastercard popup auth and callconfirmPasskey()verified— show a success message and proceed with your app flowerror— display theerrormessage with a button toretry()orstart()again
- Use
cancel()to let the user abort and reset toidleat any point.
The hook manages all card-network orchestration internally — you only need to render UI and call actions.
useInstructionVerification
Action-based hook for building a custom instruction verification UI. Render your UI based on status and call the corresponding action to advance the flow. Use this instead of verifyInstruction() when you need full control over the verification experience.
The hook handles authentication for both card networks:
- Visa: passkey authentication → completion
- Mastercard: popup-based authentication → completion
import { useInstructionVerification } from '@basis-theory/react-agentic';
function CustomInstructionUI({ agentId, instructionId }) {
const {
status,
error,
start,
confirmPasskey,
retry,
cancel,
} = useInstructionVerification({ agentId, instructionId });
// Render your own UI based on status
}
Input Props
| Prop | Type | Required | Description |
|---|---|---|---|
agentId | string | Yes | The agent ID |
instructionId | string | Yes | The instruction ID to verify |
onNewDevice | "decline" | "reenroll" | No | Behavior when a cross-device passkey mismatch is detected. Defaults to the value set on BtAiProvider. |
Statuses
| Status | Description | Available Actions |
|---|---|---|
idle | Not started | start() |
loading | Async operation in progress | — |
passkey_required | Visa passkey auth needed | confirmPasskey() |
popup_required | Mastercard popup needed | confirmPasskey(), retry() |
verified | Verification complete | — |
new_device_decline | Cross-device mismatch detected, onNewDevice is "decline" | cancel() |
new_device_reenroll | Cross-device mismatch detected, onNewDevice is "reenroll" | cancel() |
error | Something went wrong (error has message) | retry(), start() |
Return Values
| Property | Type | Description |
|---|---|---|
status | string | Current status (see table above) |
error | string | null | Error message when status is error, new_device_decline, or new_device_reenroll |
errorDetails | object | null | Structured error info when status is error. See error details object. |
start | () => Promise | Begin or restart the verification flow |
confirmPasskey | () => Promise | Trigger passkey auth (Visa) or popup authentication (Mastercard) |
retry | () => Promise | Retry the current step |
cancel | () => void | Cancel and reset to idle |
How to Build Your Own UI
- Call
start()to kick off the instruction verification flow. - Switch on
statusto render the appropriate UI for each step:loading— show a spinner or loading indicatorpasskey_required— show a prompt explaining Visa passkey authentication and callconfirmPasskey()popup_required— show a prompt explaining Mastercard popup auth and callconfirmPasskey()verified— show a success message and proceed with your app flownew_device_decline— inform the user that the card was enrolled on a different device and they must use the original device or re-enrollnew_device_reenroll— prompt the user to re-enroll the card on the current deviceerror— display theerrormessage with a button toretry()orstart()again. CheckerrorDetailsfor trace IDs when reporting issues.
- Use
cancel()to let the user abort and reset toidleat any point.
The hook manages all card-network orchestration internally — you only need to render UI and call actions.
Error Details Object
When a verification hook enters the error status due to an API error, errorDetails provides structured information for debugging. It is null for non-API errors.
| Property | Type | Description |
|---|---|---|
traceId | string | null | The BT-TRACE-ID response header from the failed API call. Include this when contacting support. |
debug | object | null | Additional debug information from the API error response (e.g., provider correlation IDs). |
code | string | null | The error code from the API (e.g., PROVIDER_VERIFICATION_FAILED). See error codes. |
status | number | null | The HTTP status code of the failed response. |
errorDetails is cleared automatically when a new action is called (e.g., start(), retry(), cancel()).
Theming
The built-in verification modals use a design token system that you can customize via the theme prop on BtAiProvider. Pass a partial theme object — unset tokens fall back to the default dark theme.
import { BtAiProvider } from '@basis-theory/react-agentic';
function App() {
return (
<BtAiProvider
apiKey="<PUBLIC_API_KEY>"
theme={{
colors: {
primary: '#1a1a1a',
text: {
primary: '#1a1a1a',
secondary: '#6b7280',
},
background: {
overlay: 'rgba(0, 0, 0, 0.5)',
surface: '#ffffff',
input: '#f9fafb',
button: {
primary: '#1a1a1a',
primaryText: '#ffffff',
},
},
border: {
default: '#d1d5db',
focus: '#1a1a1a',
},
},
}}
>
<YourApp />
</BtAiProvider>
);
}
You can also import defaultTheme to inspect the full token structure:
import { defaultTheme } from '@basis-theory/react-agentic';
console.log(defaultTheme);
Theme Tokens
The theme object supports the following token categories. All tokens are optional — only override what you need.
colors
| Token | Default | Description |
|---|---|---|
primary | #f4f4f5 | Primary accent color |
error | #ef4444 | Error state color |
success | #10b981 | Success state color |
text.primary | #ffffff | Primary text color |
text.secondary | #a1a1aa | Secondary/muted text color |
text.placeholder | #71717a | Input placeholder text color |
text.error | #fca5a5 | Error message text color |
background.overlay | rgba(0,0,0,0.85) | Modal backdrop overlay |
background.surface | rgba(13,13,15,1) | Modal surface background |
background.input | #18181b | Input field background |
background.card | rgba(23,23,26,1) | Card/panel background |
background.button.primary | #f4f4f5 | Primary button background |
background.button.primaryText | #000000 | Primary button text color |
background.button.secondary | rgba(255,255,255,0.1) | Secondary button background |
background.button.secondaryText | #ffffff | Secondary button text color |
background.button.disabled | #52525b | Disabled button background |
border.default | #52525b | Default border color |
border.focus | #f4f4f5 | Focused input border color |
border.modal | rgba(255,255,255,0.1) | Modal border color |
border.error | rgba(239,68,68,0.3) | Error state border color |
spinner.track | rgba(244,244,245,0.3) | Loading spinner track color |
spinner.indicator | rgba(244,244,245,1) | Loading spinner indicator color |
typography
| Token | Default | Description |
|---|---|---|
fontFamily | Inter, -apple-system, ... | Primary font stack |
fontSize.title | 24px | Modal title font size |
fontSize.body | 14px | Body text font size |
fontSize.small | 12px | Small text font size |
fontSize.input | 18px | OTP input font size |
fontWeight.normal | 400 | Normal font weight |
fontWeight.medium | 500 | Medium font weight |
fontWeight.semibold | 600 | Semibold font weight |
borders
| Token | Default | Description |
|---|---|---|
radius.modal | 8px | Modal border radius |
radius.input | 8px | Input field border radius |
radius.button | 8px | Button border radius |
radius.option | 8px | OTP method option border radius |
spacing
| Token | Default | Description |
|---|---|---|
modal | 32px | Modal internal padding |
section | 16px | Section spacing |
input | 8px | Input element spacing |
buttonGap | 8px | Gap between buttons |