Skip to main content

Payment Credentials

A payment credential is minted from an allowance for a specific amount, in a format the allowance rail supports. Its spendable value is returned exactly once, in the create response.

Minting commits allowance spend. The amount is drawn down when the credential is issued, and it is not returned if the credential is never presented, is rejected by the merchant, or fails authorization. There is no void or release operation.

Create a Payment Credential

POST
https://api.basistheory.com/agentic/allowances/{allowance_id}/credentials
Copy

Permissions

agentic:credential:create

Request

Create a Payment Credential
curl 'https://api.basistheory.com/agentic/allowances/alw_Xw92mKvB3dQz/credentials' \
-X 'POST' \
-H 'BT-API-KEY: <PRIVATE_API_KEY>' \
-H 'BT-IDEMPOTENCY-KEY: mint-office-supplies-25-usd-v1' \
-H 'Content-Type: application/json' \
--data '{
"rail": "agentic-token",
"provider": "vic",
"amount": { "value": "25.00", "currency": "USD" },
"credential": { "format": "card" }
}'

Headers

HeaderRequiredDescription
BT-API-KEYYesPrivate application key
BT-IDEMPOTENCY-KEYNoOpts into durable retry handling before any provider call. Without it, every request is a new mint and can spend the allowance again. See Idempotency

Request Parameters

AttributeRequiredTypeDescription
allowance_idYesstringID of the allowance to mint from
railYesstringagentic-token or spt. Must be present on the allowance with status: "active"
providerYesstringvic, agentpay, or stripe. Must be valid for the rail
credentialYesobjectThe credential request, selecting a format and supplying its payload
amountConditionalobjectRequired for every format except mpp, where it is derived from the challenge. Same validation as the allowance amount

Unknown properties are rejected, except on the MPP challenge object, which preserves extension fields.

Amount rules:

  • The currency must match the allowance currency, or the request returns 400 VALIDATION_ERROR.
  • The value cannot exceed the allowance's amount_available, or the request returns 400 ALLOWANCE_AMOUNT_EXCEEDED.
  • Only one mint can be in flight on an allowance. A concurrent operation returns 409 ALLOWANCE_CREDENTIAL_IN_PROGRESS; repeat it after the first reaches a terminal result. See Operations in Progress.
  • For mpp, supplying amount is optional. If supplied, it must match the challenge amount and currency exactly.

Credential Formats

The allowance rail's credential_formats array lists the formats available to mint. Requesting a format the rail and provider do not support returns 400 VALIDATION_ERROR.

FormatRailspayloadUse it when
cardagentic-tokenNot acceptedThe checkout takes ordinary card fields
network-tokenagentic-tokenNot acceptedThe processor accepts tokenized transactions with network dynamic data
identifiersptRequiredThe recipient business accepts that processor's tokens
mppagentic-token, sptRequiredThe merchant challenges for payment over HTTP and the agent presents the credential directly

card

A network-issued virtual card credential with a dynamic security code. Takes no payload.

Request Body
{
"rail": "agentic-token",
"provider": "vic",
"amount": { "value": "25.00", "currency": "USD" },
"credential": { "format": "card" }
}
credential.value
{
"number": "4000001000004242",
"expiration_month": 12,
"expiration_year": 2030,
"cvc": "123"
}

The returned number is a network-issued token, not the source card's PAN; the expiration is the token's own; cvc is single-use dynamic data generated for this credential. Mastercard supplies a dynamic card security code for this format; Visa returns its instruction credential default.

network-token

A network payment token with single-use dynamic data, for recipients and processors that accept tokenized transactions. Takes no payload.

Request Body
{
"rail": "agentic-token",
"provider": "vic",
"amount": { "value": "25.00", "currency": "USD" },
"credential": { "format": "network-token" }
}
credential.value
{
"payment_token": "4000001000004242",
"expiration_month": 6,
"expiration_year": 2034,
"eci": "07",
"cryptogram": {
"type": "CARD_APPLICATION_CRYPTOGRAM_SHORT_FORM",
"value": "AmDDBjkH/4A=",
"expires_at": "2030-07-06T18:00:00.000Z"
}
}
AttributeTypeDescription
payment_tokenstringNetwork payment token, sometimes called a DPAN
expiration_monthintegerToken expiration month. The token's own expiry, not the source card's
expiration_yearintegerFour-digit token expiration year
ecistringNetwork-supplied electronic commerce indicator, returned as given. A missing value is normalized to 06
cryptogram.typestringAlways CARD_APPLICATION_CRYPTOGRAM_SHORT_FORM
cryptogram.valuestringSingle-use dynamic data for this transaction
cryptogram.expires_atstringNetwork-supplied cryptogram expiration when available. Mastercard does not supply one for this cryptogram type, so the allowance expiration is used

cryptogram.type is not a caller input and does not vary by network. Basis Theory requests the network-native type, TAVV from Visa and CARD_APPLICATION_CRYPTOGRAM_SHORT_FORM from Mastercard, and publishes CARD_APPLICATION_CRYPTOGRAM_SHORT_FORM for both. The normalization is deliberate: the Machine Payments Protocol defines dynamicDataType as a closed EMV-SRC enumeration that does not include Visa's proprietary TAVV, so publishing the native Visa value would emit an illegal value to a conforming consumer.

provider is the network discriminator; cryptogram.type is Basis Theory's normalized response enum. Read provider to determine the issuing network, and do not branch on cryptogram.type to infer it. An mpp credential carries the same normalized value in its encrypted dynamicDataType.

The cryptogram is transaction-scoped. The credential's expires_at is the earliest of the cryptogram's expiry and the allowance's expires_at.

identifier

The processor's own agentic token, returned as its identifier string. On the spt rail with provider stripe, this is a Stripe Shared Payment Token.

Request Body
{
"rail": "spt",
"provider": "stripe",
"amount": { "value": "10.00", "currency": "USD" },
"credential": {
"format": "identifier",
"payload": { "network_business_profile": "np_acme_store" }
}
}
AttributeRequiredTypeDescription
payload.network_business_profileYesstringNon-empty. Stripe Business Network Profile ID of the recipient, which scopes the token to that business. No other property is accepted

credential.value is the token string, such as spt_1RgaZcFPC5QUO6ZCDVZuVA8q.

mpp

A complete Machine Payments Protocol credential, built from a payment challenge the merchant advertised in a WWW-Authenticate header. credential.value is a base64url string to be sent as Authorization: Payment <credential.value>.

Request Body
{
"rail": "spt",
"provider": "stripe",
"credential": {
"format": "mpp",
"payload": {
"challenge": {
"id": "ch_card_123",
"realm": "api.acme.example",
"method": "stripe",
"intent": "charge",
"request": "eyJhbW91bnQiOiIyNTAwIiwiY3VycmVuY3kiOiJ1c2QiLCJtZXRob2REZXRhaWxzIjp7Im5ldHdvcmtJZCI6Im5wX2FjbWVfc3RvcmUifX0",
"expires": "2030-07-07T18:30:00.000Z"
}
}
}
}

The example request decodes to:

{
"amount": "2500",
"currency": "usd",
"methodDetails": { "networkId": "np_acme_store" }
}
AttributeRequiredTypeDescription
payload.challengeYesobjectThe merchant's challenge. See challenge object
payload.billing_addressConditionalobjectMPP Card only. Required when the decoded challenge sets methodDetails.billingRequired to true, otherwise optional. See billing address object
payload.cardholder_full_nameNostringNon-empty. MPP Card only. Supply when known

billing_address and cardholder_full_name are rejected for any method other than card. The API uses snake_case; the returned wire credential uses the specification's billingAddress, cardholderFullName, and countryCode names.

Challenge Object
AttributeRequiredTypeDescription
idYesstringNon-empty challenge identifier
realmYesstringNon-empty challenge realm
methodYesstringstripe, spt, or card. The agentic-token rail requires card; the spt rail requires stripe or spt
intentYesstringExactly charge. No other intent is supported
requestYesstringUnpadded base64url-encoded JSON object. See decoded request
expiresNostringRFC 3339 timestamp. A challenge already past its expiry is rejected
digestNostringPassed through
descriptionNostringPassed through
opaqueNostringPassed through

Unrecognized challenge properties are preserved and round-trip into the returned credential.

method: "stripe" is the registered Stripe dialect and produces payload.spt in the credential. Prefer it.

method: "spt" is an experimental Basis Theory extension. It is not part of the MPP specification and carries no interoperability guarantee: it produces payload.sharedPaymentToken and payload.processor, which a conforming MPP consumer is not required to understand. Use it only when a merchant challenges with that dialect, and do not build a checkout around it.

Decoded Request Validation

The decoded request object is validated before any provider call.

FieldRule
amountPositive integer string in base units, so "4999" means 49.99 in a two-decimal currency
currencyLowercase ISO 4217 code that Basis Theory supports as a payment currency
expiresMust not be present. The expiry belongs on the challenge, not inside the encoded request
EncodingMust be unpadded base64url. For method: "card", the JSON must additionally be serialized with the JSON Canonicalization Scheme

For method: "card", methodDetails is required and must contain:

FieldRule
acceptedNetworksNon-empty array of strings that must include the selected provider's network, visa for vic or mastercard for agentpay
merchantNameNon-empty string
encryptionJwk or jwksUri with kidExactly one of the two. An embedded key cannot be combined with jwksUri, and a top-level kid cannot accompany an embedded encryptionJwk. Keys must be RSA-OAEP-256
billingRequiredBoolean when present

The merchant owns the recipient key pair and publishes only its public key in the challenge. The selected JWK must contain kty: "RSA", use: "enc", alg: "RSA-OAEP-256", a non-empty kid, and base64url n and e values for an RSA key of at least 2048 bits. The resulting compact JWE uses RSA-OAEP-256 key wrapping and A256GCM content encryption.

Basis Theory Client Encryption Keys from POST /keys are X25519 keys for ECDH-ES and are not compatible with MPP Card, which requires an RSA JWK. A merchant starting from an RSA SPKI PEM must publish its public components as a JWK in encryptionJwk or the advertised JWKS.

Remote key retrieval is constrained: the jwksUri origin must match challenge.realm, redirects are refused, private, loopback, link-local, carrier-NAT, and reserved addresses are refused in both IPv4 and IPv6 form, the response is fetched under a five-second timeout, and the body is capped at 64 KiB.

A jwksUri that violates one of those constraints, or a JWKS that does not contain the requested kid, returns 400 VALIDATION_ERROR naming the offending field. A key that could not be fetched at all returns 502 MPP_KEY_UNAVAILABLE.

The recipient profile is resolved entirely from the decoded request object. Every path below is a field inside that merchant-supplied payload, not a field on the Basis Theory allowance resource, which has no recipientId.

methodRecipient profile is read from
striperequest.methodDetails.networkId
sptrequest.allowance.recipientId, falling back to request.methodDetails.recipient.id

For method: "spt", a request.methodDetails.processors array that does not offer stripe is rejected. A challenge with no resolvable recipient profile is rejected.

Billing Address Object

At least one property is required. Unknown properties are rejected.

AttributeRequiredTypeDescription
line1NostringNon-empty
line2NostringNon-empty
cityNostringNon-empty
stateNostringNon-empty
zipNostringNon-empty
country_codeNostringISO 3166-1 alpha-2 code

Response

Returns a payment credential object with 201.

Response
{
"id": "cred_pQ8vNx2LmRs4",
"rail": "agentic-token",
"provider": "vic",
"amount": { "value": "25.00", "currency": "USD" },
"credential": {
"format": "card",
"value": {
"number": "4000001000004242",
"expiration_month": 12,
"expiration_year": 2030,
"cvc": "123"
}
},
"provider_ids": { "transaction_reference_id": "vic_txn_9a1c" },
"expires_at": "2030-07-08T00:00:00.000Z"
}
credential.value is returned only by this response and is never persisted. A GET returns metadata without it, and replaying the same supplied BT-IDEMPOTENCY-KEY returns 409 CREDENTIAL_PAYLOAD_UNAVAILABLE. Capture it when it arrives. A keyless retry is a new mint and may spend the allowance again.

For an identifier credential, or an mpp credential that embeds one, the processor's token is returned only as credential.value. It is not persisted, logged, copied into provider_ids, or available to support.

Errors: 400 VALIDATION_ERROR, 400 RAIL_NOT_FOUND, 400 RAIL_NOT_ACTIVE, 400 ALLOWANCE_AMOUNT_EXCEEDED, 400 ALLOWANCE_EXPIRED, 400 ALLOWANCE_CANCELLED, 400 PAYMENT_METHOD_DELETED, 404 ALLOWANCE_NOT_FOUND, 404 PAYMENT_METHOD_NOT_FOUND, 409 IDEMPOTENCY_CONFLICT, 409 IDEMPOTENCY_IN_PROGRESS, 409 CREDENTIAL_PAYLOAD_UNAVAILABLE, 409 CREDENTIAL_OUTCOME_UNKNOWN, 409 ALLOWANCE_CREDENTIAL_IN_PROGRESS, 409 ALLOWANCE_UPDATE_IN_PROGRESS, 409 ALLOWANCE_VERIFICATION_IN_PROGRESS, 409 ALLOWANCE_DELETE_IN_PROGRESS, 422 PROVIDER_CREDENTIALS_FAILED, 502 MPP_KEY_UNAVAILABLE.

Because a mint 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. All three are described in Operations in Progress.

List Payment Credentials for an Allowance

GET
https://api.basistheory.com/agentic/allowances/{allowance_id}/credentials
Copy

Permissions

agentic:credential:get

Request

List Payment Credentials
curl 'https://api.basistheory.com/agentic/allowances/alw_Xw92mKvB3dQz/credentials' \
-H 'BT-API-KEY: <PRIVATE_API_KEY>'

Query Parameters

ParameterRequiredTypeDefaultDescription
sizeNointeger201 through 100
startNostringOpaque cursor from the previous response's pagination.next

Response

Returns a paginated list of credential metadata objects. Spendable values are never included.

Get Payment Credential Metadata

GET
https://api.basistheory.com/agentic/allowances/{allowance_id}/credentials/{credential_id}
Copy

Permissions

agentic:credential:get

Request

Get Payment Credential Metadata
curl 'https://api.basistheory.com/agentic/allowances/alw_Xw92mKvB3dQz/credentials/cred_pQ8vNx2LmRs4' \
-H 'BT-API-KEY: <PRIVATE_API_KEY>'

Response

Returns a credential metadata object, or 404 PAYMENT_CREDENTIAL_NOT_FOUND.

List Payment Credentials for a Tenant

Returns credential metadata across the whole tenant.

GET
https://api.basistheory.com/agentic/payment-credentials
Copy

Permissions

agentic:credential:get

Request

List Tenant Payment Credentials
curl 'https://api.basistheory.com/agentic/payment-credentials?size=50' \
-H 'BT-API-KEY: <PRIVATE_API_KEY>'

Query Parameters

ParameterRequiredTypeDefaultDescription
sizeNointeger201 through 100
startNostringOpaque cursor from the previous response's pagination.next

Response

Returns a paginated list of credential metadata objects.

Payment Credential Object

Returned only by the create call.

AttributeTypeDescription
idstringPrefixed with cred_
railstringThe rail the credential was minted on
providerstringThe provider the credential was minted through. See Rails and Providers
amountobjectThe amount drawn from the allowance
credential.formatstringThe minted credential format
credential.valueobject or stringThe spendable value. An object for card and network-token, a string for identifier and mpp. Returned once and never persisted
provider_idsobjectSafe provider-native references for support. Never contains a spendable value
expires_atstringThe earliest of the allowance expiration, the MPP challenge expiry when one applies, and any provider-imposed credential expiry such as a cryptogram's

Payment Credential Metadata Object

Returned by every read. Never contains a spendable value — no card numbers, network tokens, cryptograms, processor token values, or MPP payloads.

AttributeTypeDescription
idstringPrefixed with cred_
allowance_idstringThe allowance the credential was minted from
payment_method_idstringThe payment method backing that allowance
railstringThe rail the credential was minted on
providerstringThe provider the credential was minted through. See Rails and Providers
formatstringThe minted credential format
amountobjectThe amount drawn from the allowance
statusstringAlways created
provider_idsobjectSafe provider-native references for support
expires_atstringISO 8601 timestamp
created_atstringISO 8601 timestamp

status has exactly one value because Basis Theory has no settlement visibility: a credential is never observed being spent, so it never becomes approved, declined, or settled. Diagnose checkout outcomes with the merchant and whichever party processed the payment, such as its acquirer or your processor. See Errors and Recovery for the identifiers to send if you escalate.