Allowance Verification
Verification is how a customer approves one allowance rail with its provider. One endpoint drives every step of every provider's ceremony: you send an action, receive a state, and continue until the state is active. Most integrations drive it with the Web Agentic SDK or by following Own the Browser Verification Flow.
Verify an Allowance Rail
Permissions
agentic:allowance:verify
Available to public and private applications.
Request
curl 'https://api.basistheory.com/agentic/allowances/alw_Xw92mKvB3dQz/verify' \
-X 'POST' \
-H 'BT-API-KEY: <PUBLIC_API_KEY>' \
-H 'Content-Type: application/json' \
--data '{
"rail": "agentic-token",
"provider": "vic",
"action": "start",
"display_name": "Example Agent",
"device_context": {
"language_code": "en-US",
"time_zone": "America/New_York",
"platform_type": "WEB"
}
}'
Headers
| Header | Required | Description |
|---|---|---|
BT-API-KEY | Yes | Public or private application key with agentic:allowance:verify. Use a public key in browser code; never ship a private key to the browser |
Request Parameters
| Attribute | Required | Type | Description |
|---|---|---|---|
allowance_id | Yes | string | ID of the allowance whose rail is being verified |
rail | Yes | string | The rail to verify. Only agentic-token has a verification ceremony |
provider | Yes | string | vic or agentpay for agentic-token, stripe for spt. Must match the rail on the allowance |
action | No | string | Defaults to start. See actions |
Each action accepts only its own additional fields; unknown properties or fields from a different action return 400 VALIDATION_ERROR. The spt rail has no verification ceremony and its allowance rails are active from creation, so rail: "spt" returns 400 VERIFICATION_NOT_REQUIRED.
Actions
action | Provider | Additional fields | Typical response |
|---|---|---|---|
start | vic, agentpay | display_name, device_context | Visa returns passkey_session; Mastercard returns redirect |
submit_session | vic | session_context | passkey when the device is bound, otherwise select_otp_method |
select_otp_method | vic | method_id | otp |
submit_otp | vic | otp_code | passkey with a REGISTER context |
submit_passkey | vic | assurance_data | status: "active" |
complete | agentpay | — | status: "active", or verification_required while Mastercard is still processing |
Visa exposes no polling action; each returned next_action must be performed.
start
| Attribute | Required | Type | Description |
|---|---|---|---|
display_name | No | string | 1 to 60 characters. Defaults to Agent. The application or agent name the customer sees on provider-hosted screens, such as "Add Allowance to Example Agent". Sent once on start and retained for the rest of the ceremony |
device_context | No | object | Browser and device data the provider uses for risk assessment. See device context |
For Mastercard, display_name is sent as the requestor name and presentation name, and the allowance's merchant.name is used as the merchant name.
Device Context Object
Only language_code and time_zone are validated; every other property is forwarded to Visa unchanged. The object is retained server-side and reused on submit_session.
| Attribute | Required | Type | Description |
|---|---|---|---|
language_code | No | string | BCP 47 language tag, canonicalized on input, so EN-us becomes en-US. Underscore locales such as en_US are rejected |
time_zone | No | string | IANA time zone identifier, such as America/New_York |
Fields Visa expects, forwarded as sent: screen_height, screen_width, user_agent_string, java_script_enabled, client_device_id, client_reference_id, platform_type (WEB, MOBILE, or NATIVE), color_depth, accept_header, and ip_address. Send ip_address as IPv4 only; Visa rejects IPv6 and Basis Theory does not rewrite the value.
submit_session
| Attribute | Required | Type | Description |
|---|---|---|---|
session_context | Yes | object | Exact object containing only secure_token |
session_context.secure_token | Yes | string | Non-empty. The secure session token returned by Visa's hosted iframe after a passkey_session action |
The device context and display name sent on start are not resent.
select_otp_method
| Attribute | Required | Type | Description |
|---|---|---|---|
method_id | Yes | string | Non-empty. The id of a method from the select_otp_method next action |
submit_otp
| Attribute | Required | Type | Description |
|---|---|---|---|
otp_code | Yes | string | Non-empty. The code the cardholder received |
submit_passkey
| Attribute | Required | Type | Description |
|---|---|---|---|
assurance_data | Yes | object | The result of Visa's passkey ceremony. Basis Theory validates it with Visa before activating the rail |
Visa returns this data in camelCase and the API expects snake_case:
{
"identifier": "0198f2a1",
"dfp_session_id": "8c1d4e77",
"fido_assertion_data": {
"code": "eyJhbGciOiJFUzI1NiJ9"
}
}
complete
Takes no additional fields. Retrieves Mastercard's managed-authentication result server to server and activates the rail only when Mastercard confirms it.
complete is the only way to finish a Mastercard ceremony. The mastercard_verification_complete browser message signals that the hosted ceremony returned, not its result; call complete even when the message is lost.
Response
Returns 200 with a verification envelope. Continue until status is active.
{
"status": "verification_required",
"rail": "agentic-token",
"provider": "vic",
"next_action": {
"type": "passkey_session",
"embed": {
"iframe_url": "https://auth.example-visa.com/embed",
"api_key": "<VISA_PUBLISHABLE_KEY>",
"client_app_id": "<VISA_CLIENT_APP_ID>"
}
}
}
| Attribute | Type | Description |
|---|---|---|
status | string | verification_required or active |
rail | string | The rail that was verified |
provider | string | The provider that was verified |
next_action | object | Present when the provider requires a browser or customer action. See next actions |
A rail that is already active returns its verification envelope without contacting the provider:
{
"status": "active",
"rail": "agentic-token",
"provider": "vic"
}
A verification_required response without next_action means the provider is still processing; wait briefly and repeat the action. Only Mastercard's complete responds this way. Every other verification_required response contains the action the client must perform.
Basis Theory confirms every terminal transition with the provider server to server. No value a client sends can activate a rail on its own.
Next Actions
type | Contains | What the client does |
|---|---|---|
passkey_session | embed | Mount Visa's hidden session iframe, create a session, then send its secure token with submit_session |
select_otp_method | methods | Present the choices and send back method_id |
otp | method, code_expiration_minutes, max_attempts | Collect the code and send back otp_code |
passkey | passkey_context, embed | From a user gesture, open Visa's visible ceremony in a popup and send back assurance_data |
redirect | uri, uri_type, purpose, expires_at | Open Mastercard's page top-level in a popup or new tab, then call complete |
passkey_session
Visa's hidden session-initialization surface. It only establishes the session and returns a secure token; it must not be used for the visible passkey ceremony.
| Attribute | Type | Description |
|---|---|---|
embed.iframe_url | string | URL of Visa's hosted authentication page |
embed.api_key | string | Publishable-class Visa key, served by the API and subject to rotation |
embed.client_app_id | string | Visa client application identifier |
select_otp_method
| Attribute | Type | Description |
|---|---|---|
methods | array | Available one-time-code destinations. Each has id, type (sms, email, or otponlinebanking), and value holding a masked destination or the issuer's display label |
Visa step-up methods that are not code-based, including app-to-app, customer-service, and outbound-call flows, are not supported and are filtered out of this list. When Visa offers no supported method, the request returns 422 VERIFICATION_NOT_SUPPORTED.
otp
| Attribute | Type | Description |
|---|---|---|
method | object | The selected method, with id, type, and value |
code_expiration_minutes | integer | Mapped from Visa's response. Defaults to 5 when Visa omits it |
max_attempts | integer | Mapped from Visa's response. Defaults to 3 when Visa omits it |
Both values are display hints; Visa enforces them when it validates the code.
passkey
Visa's visible ceremony. Open it in a popup created synchronously from the cardholder's click to preserve browser user activation and WebAuthn permissions.
| Attribute | Type | Description |
|---|---|---|
embed | object | Same fields as passkey_session. The same hosted URL backs both surfaces |
passkey_context.endpoint | string | Visa ceremony endpoint |
passkey_context.identifier | string | Visa ceremony identifier |
passkey_context.payload | string | Opaque Visa ceremony payload. Pass through unmodified |
passkey_context.action | string | REGISTER or AUTHENTICATE. Visa chooses; the client must branch on it |
passkey_context.platform_type | string | Platform Visa expects, such as WEB |
passkey_context.auth_preferences.response_mode | string | Forward exactly as returned |
passkey_context.auth_preferences.response_type | string | Forward exactly as returned |
REGISTER and AUTHENTICATE use the same visible popup surface. After a REGISTER ceremony completes, the passkey exists but the mandate is not authenticated: send start again to open a fresh session, which lands in the AUTHENTICATE branch.
Basis Theory requests Visa's popup response preference for both ceremonies; overriding response_mode with a popup-incompatible value causes Visa to reject the ceremony.
redirect
Mastercard's managed-authentication ceremony.
| Attribute | Type | Description |
|---|---|---|
uri | string | Hosted ceremony URL |
uri_type | string | WEB_URI |
purpose | string | The ceremony this redirect performs. Always present; always transaction_authentication |
expires_at | string | ISO 8601 expiry of the authentication session, when the provider supplies one |
Open uri top-level, in a popup or a new tab; Mastercard's page sends X-Frame-Options: DENY and cannot be embedded in an iframe. The managed ceremony creates a passkey when the cardholder does not have one, so there is no separate registration step.
Errors
| Code | HTTP | Meaning |
|---|---|---|
VALIDATION_ERROR | 400 | Unknown property, wrong fields for the action, or an action the provider does not support |
VERIFICATION_STATE_INVALID | 400 | The action is out of order for the current state. Restart from start |
VERIFICATION_NOT_REQUIRED | 400 | That rail has no verification ceremony, so there is nothing to verify. Mint from it directly |
INVALID_OTP | 400 | The code is wrong or expired. Retry the step; the provider enforces the attempt limit |
RAIL_NOT_FOUND | 400 | That rail and provider pair is not on the allowance |
ALLOWANCE_CANCELLED | 400 | The allowance was cancelled |
ALLOWANCE_EXPIRED | 400 | The allowance expiration has passed |
PAYMENT_METHOD_DELETED | 400 | The backing payment method was deleted |
ALLOWANCE_NOT_FOUND | 404 | No such allowance in the tenant |
ALLOWANCE_VERIFICATION_IN_PROGRESS | 409 | Another verification call is running. Retry shortly |
ALLOWANCE_UPDATE_IN_PROGRESS, ALLOWANCE_DELETE_IN_PROGRESS, ALLOWANCE_CREDENTIAL_IN_PROGRESS | 409 | Another operation holds the allowance. Retry shortly |
VERIFICATION_NOT_SUPPORTED | 422 | The source cannot complete this ceremony, such as a Mastercard without managed authentication or a Visa card with no supported code method. The spt rail remains available |
PROVIDER_VERIFICATION_FAILED | 422 | The provider rejected or expired the session, or rejected the passkey assurance data. Restart from start |
PROVIDER_NOT_CONFIGURED | 422 | Tenant onboarding for this rail is incomplete. Contact us |
Because verification takes the payment method's operation slot, it can also return 409 PAYMENT_METHOD_OPERATION_IN_PROGRESS, 409 PAYMENT_METHOD_DELETE_IN_PROGRESS, or 409 PAYMENT_METHOD_DELETE_INCOMPLETE.
See Errors and Recovery for what is safe to retry.