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.
Create a Payment Credential
Permissions
agentic:credential:create
Request
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
| Header | Required | Description |
|---|---|---|
BT-API-KEY | Yes | Private application key |
BT-IDEMPOTENCY-KEY | No | Opts 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
| Attribute | Required | Type | Description |
|---|---|---|---|
allowance_id | Yes | string | ID of the allowance to mint from |
rail | Yes | string | agentic-token or spt. Must be present on the allowance with status: "active" |
provider | Yes | string | vic, agentpay, or stripe. Must be valid for the rail |
credential | Yes | object | The credential request, selecting a format and supplying its payload |
amount | Conditional | object | Required 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 returns400 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, supplyingamountis 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.
| Format | Rails | payload | Use it when |
|---|---|---|---|
card | agentic-token | Not accepted | The checkout takes ordinary card fields |
network-token | agentic-token | Not accepted | The processor accepts tokenized transactions with network dynamic data |
identifier | spt | Required | The recipient business accepts that processor's tokens |
mpp | agentic-token, spt | Required | The 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.
{
"rail": "agentic-token",
"provider": "vic",
"amount": { "value": "25.00", "currency": "USD" },
"credential": { "format": "card" }
}
{
"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.
{
"rail": "agentic-token",
"provider": "vic",
"amount": { "value": "25.00", "currency": "USD" },
"credential": { "format": "network-token" }
}
{
"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"
}
}
| Attribute | Type | Description |
|---|---|---|
payment_token | string | Network payment token, sometimes called a DPAN |
expiration_month | integer | Token expiration month. The token's own expiry, not the source card's |
expiration_year | integer | Four-digit token expiration year |
eci | string | Network-supplied electronic commerce indicator, returned as given. A missing value is normalized to 06 |
cryptogram.type | string | Always CARD_APPLICATION_CRYPTOGRAM_SHORT_FORM |
cryptogram.value | string | Single-use dynamic data for this transaction |
cryptogram.expires_at | string | Network-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.
{
"rail": "spt",
"provider": "stripe",
"amount": { "value": "10.00", "currency": "USD" },
"credential": {
"format": "identifier",
"payload": { "network_business_profile": "np_acme_store" }
}
}
| Attribute | Required | Type | Description |
|---|---|---|---|
payload.network_business_profile | Yes | string | Non-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>.
{
"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" }
}
| Attribute | Required | Type | Description |
|---|---|---|---|
payload.challenge | Yes | object | The merchant's challenge. See challenge object |
payload.billing_address | Conditional | object | MPP Card only. Required when the decoded challenge sets methodDetails.billingRequired to true, otherwise optional. See billing address object |
payload.cardholder_full_name | No | string | Non-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
| Attribute | Required | Type | Description |
|---|---|---|---|
id | Yes | string | Non-empty challenge identifier |
realm | Yes | string | Non-empty challenge realm |
method | Yes | string | stripe, spt, or card. The agentic-token rail requires card; the spt rail requires stripe or spt |
intent | Yes | string | Exactly charge. No other intent is supported |
request | Yes | string | Unpadded base64url-encoded JSON object. See decoded request |
expires | No | string | RFC 3339 timestamp. A challenge already past its expiry is rejected |
digest | No | string | Passed through |
description | No | string | Passed through |
opaque | No | string | Passed 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.
| Field | Rule |
|---|---|
amount | Positive integer string in base units, so "4999" means 49.99 in a two-decimal currency |
currency | Lowercase ISO 4217 code that Basis Theory supports as a payment currency |
expires | Must not be present. The expiry belongs on the challenge, not inside the encoded request |
| Encoding | Must 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:
| Field | Rule |
|---|---|
acceptedNetworks | Non-empty array of strings that must include the selected provider's network, visa for vic or mastercard for agentpay |
merchantName | Non-empty string |
encryptionJwk or jwksUri with kid | Exactly 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 |
billingRequired | Boolean 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.
method | Recipient profile is read from |
|---|---|
stripe | request.methodDetails.networkId |
spt | request.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.
| Attribute | Required | Type | Description |
|---|---|---|---|
line1 | No | string | Non-empty |
line2 | No | string | Non-empty |
city | No | string | Non-empty |
state | No | string | Non-empty |
zip | No | string | Non-empty |
country_code | No | string | ISO 3166-1 alpha-2 code |
Response
Returns a payment credential object with 201.
{
"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
Permissions
agentic:credential:get
Request
curl 'https://api.basistheory.com/agentic/allowances/alw_Xw92mKvB3dQz/credentials' \
-H 'BT-API-KEY: <PRIVATE_API_KEY>'
Query Parameters
| Parameter | Required | Type | Default | Description |
|---|---|---|---|---|
size | No | integer | 20 | 1 through 100 |
start | No | string | — | Opaque 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
Permissions
agentic:credential:get
Request
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.
Permissions
agentic:credential:get
Request
curl 'https://api.basistheory.com/agentic/payment-credentials?size=50' \
-H 'BT-API-KEY: <PRIVATE_API_KEY>'
Query Parameters
| Parameter | Required | Type | Default | Description |
|---|---|---|---|---|
size | No | integer | 20 | 1 through 100 |
start | No | string | — | Opaque 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.
| Attribute | Type | Description |
|---|---|---|
id | string | Prefixed with cred_ |
rail | string | The rail the credential was minted on |
provider | string | The provider the credential was minted through. See Rails and Providers |
amount | object | The amount drawn from the allowance |
credential.format | string | The minted credential format |
credential.value | object or string | The spendable value. An object for card and network-token, a string for identifier and mpp. Returned once and never persisted |
provider_ids | object | Safe provider-native references for support. Never contains a spendable value |
expires_at | string | The 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.
| Attribute | Type | Description |
|---|---|---|
id | string | Prefixed with cred_ |
allowance_id | string | The allowance the credential was minted from |
payment_method_id | string | The payment method backing that allowance |
rail | string | The rail the credential was minted on |
provider | string | The provider the credential was minted through. See Rails and Providers |
format | string | The minted credential format |
amount | object | The amount drawn from the allowance |
status | string | Always created |
provider_ids | object | Safe provider-native references for support |
expires_at | string | ISO 8601 timestamp |
created_at | string | ISO 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.