Network Tokens Implementation
Enterprise-ready network token payments with reduced PCI scope and issuer trust.Network tokenization replaces raw card numbers with issuer-managed tokens for secure reuse, enabling flexible, PSP-agnostic payment flows with full programmatic control over token lifecycle, authentication, and processor routing—without requiring every transaction to pass through the vault.
This guide covers the payment lifecycle with Basis Theory Network Tokens, from provisioning and the first customer-initiated transaction (CIT) to subsequent merchant-initiated transactions (MITs), including processor integrations that keep sensitive card data out of your environment and aligned with issuer expectations.
If you are not yet collecting your customers' cards with Basis Theory, here are a few guides you can explore:
- Replace Processor iFrames - capture new cards in the frontend;
- Recollect Security Code - patch existing cards with CVC/CVV in the frontend;
- Receive Cards via API / Webhooks - receive cards in API requests;
Getting Started
To get started, you will need to create a Basis Theory Account and a TEST Tenant.
Private Application
You will need a Private Application to allow your backend to call Basis Theory APIs. Click here to create one using the Basis Theory Customer Portal.
This will create an application with the following Access Controls:
- Permissions:
network-token:create,network-token:reveal,proxy:invoke
key from the created Private Application as it will be used later in this guide.Provision a Network Token
After a card has been collected from the user, merchants can provision Network Tokens against the card networks by calling the Create a Network Token API while passing either the token_intent_id or the token_id. This step is performed only once per card.
- Token Intent
- Token
curl 'https://api.basistheory.com/network-tokens' \
-X 'POST' \
-H 'BT-API-KEY: <PRIVATE_API_KEY>' \
-H 'Content-Type: application/json' \
-d '{
"token_intent_id": "d2cbc1b4-5c3a-45a3-9ee2-392a1c475ab4",
"cardholder_info": {
"name": "John Doe",
"address": {
"line1": "123 Main Street",
"line2": "Apt 4B",
"line3": "Building 7",
"postal_code": "90210",
"city": "Beverly Hills",
"state_code": "CA",
"country_code": "USA"
}
}
}'
curl 'https://api.basistheory.com/network-tokens' \
-X 'POST' \
-H 'BT-API-KEY: <PRIVATE_API_KEY>' \
-H 'Content-Type: application/json' \
-d '{
"token_id": "1cd9c8bf-99fd-4784-ba69-68b15b301019",
"cardholder_info": {
"name": "John Doe",
"address": {
"line1": "123 Main Street",
"line2": "Apt 4B",
"line3": "Building 7",
"postal_code": "90210",
"city": "Beverly Hills",
"state_code": "CA",
"country_code": "USA"
}
}
}'
If the card information is correct and the issuer supports it, a new Network Token will be generated:
{
"id": "1a97a7f6-5d7e-4a8e-ad08-c2472cfedf7f",
"tenant_id": "4aee08b9-5557-474b-a120-252e01fc7b0f",
"data": {
"number": "4446878164352273",
"expiration_month": 11,
"expiration_year": 2030
},
"card": {
"last4": "0002",
"expiration_month": 10,
"expiration_year": 2027
},
"network_token": {
"bin": "444687",
"last4": "2273",
"expiration_month": 11,
"expiration_year": 2030,
"brand": "visa",
"funding": "credit",
"authentication": "sca_required",
"issuer": {
"country": "GB",
"name": "3DS Test Cards (TEST)"
},
"issuer_country": {
"alpha2": "GB",
"name": "CENTRAL FEDERAL SAVINGS AND LOAN ASSOCIATION",
"numeric": "826"
},
"segment": "Commercial"
},
"par": "V2831416512995186306443704057",
"status": "active",
"created_by": "0bc89db5-fadd-4d57-af93-10472e35ebd3",
"created_at": "2025-03-10T14:23:56.5580574+00:00"
}
What you should store in your database:
- [Required] The Network Token
id, associating it with the rightful customer account. - The Network Token
data, enabling you to process payments without having to fetch it later.
Active Card Check
Network Token provisioning can act as an early validation step during card onboarding, indicating that a card is real, recognized by the network, and eligible for tokenized transactions. Successful provisioning reflects issuer participation at tokenization time and may help reduce false-positive declines in later payments.
However, provisioning is not a payment verification mechanism and is subject to coverage limitations. A card may be valid and payment-ready yet still be ineligible for network tokenization due to issuer, network, region, or use-case constraints. Provisioning does NOT:
- Authorize a transaction
- Confirm funds availability
- Validate CVC or expiry with transaction-level guarantees
- Establish cardholder intent or provide liability shift
- Does not replace a $0 or small-amount authorization where required
Test Cases and Error Handling
Network Token provisioning can fail for issuer, network, or data-related reasons, and should be handled explicitly in your integration. We recommend implementing the scenarios outlined in our Network Token test cases and error handling documentation to ensure clear remediation paths, appropriate retries, and a smooth user experience.
First Charge
Once a Network Token has been provisioned, merchants can process customer-initiated transactions (CITs) using the token in place of the card number, allowing issuers to apply dynamic authentication and token-specific controls during authorization.
Generating a Cryptogram
A cryptogram is a transaction-specific, one-time security value generated by the issuer for a Network Token and is commonly required for customer-initiated transaction authorizations. It allows the issuer to verify that the transaction is fresh, bound to the specific request, and authorized in real time by the customer, providing stronger protection than static card data alone.
This is done by calling the Generate a Cryptogram API passing the Network Token id:
curl 'https://api.basistheory.com/network-tokens/<NETWORK_TOKEN_ID>/cryptogram' \
-X 'POST' \
-H 'BT-API-KEY: <PRIVATE_API_KEY>' \
-H 'Content-Type: application/json'
Which returns a cryptogram and associated ECI:
{
"cryptogram": "5hgXjWJBDkiONPQ6EFxtgA==",
"eci": "07"
}
Processing Payment
Now that you have a Network Token and a cryptogram, both handled outside of PCI scope, you can route these credentials directly to your payment processor or acquirer API for authorization:
- Checkout.com
- Stripe
- Adyen
- Braintree
In Checkout.com, you can charge with a Network Token by calling the /payments API.
curl 'https://api.sandbox.checkout.com/payments' \
-X 'POST' \
-H 'Authorization: Bearer <CHECKOUT_SECRET_KEY>' \
-H 'Content-Type: application/json' \
--data '{
"source": {
"type": "network_token",
"token_type": "vts",
"number": "4446878164352273",
"expiry_month": "11",
"expiry_year": "2030",
"eci": "07",
"cryptogram": "5hgXjWJBDkiONPQ6EFxtgA=="
},
"amount": 1000,
"currency": "USD",
"processing_channel_id": "pc_svyx12xvaaduraq3ckb2gwnvzq"
}'
{
"id": "pay_myvj6zxrt45edgrswrliyxbtnq",
"approved": true,
"status": "Authorized",
"amount": 1000,
"currency": "USD",
"source": {
"type": "card",
"id": "src_gguer4xlawke3ctljczft7573a",
...
},
"scheme_id": "647537243670773",
...
}
In Stripe, you can charge with a Network Token by creating a Payment Intent.
curl https://api.stripe.com/v1/payment_intents \
-H "Authorization: Bearer <STRIPE_SECRET_KEY>" \
-d "amount=1000" \
-d "currency=usd" \
-d "confirm=true" \
-d "payment_method_types[]=card" \
-d "payment_method_data[type]=card" \
-d "payment_method_data[card][exp_month]=11" \
-d "payment_method_data[card][exp_year]=2030" \
-d "payment_method_data[card][last4]=2273" \
-d "payment_method_data[card][network_token][number]=4446878164352273" \
-d "payment_method_data[card][network_token][exp_month]=11" \
-d "payment_method_data[card][network_token][exp_year]=2030" \
-d "payment_method_options[card][network_token][cryptogram]=5hgXjWJBDkiONPQ6EFxtgA==" \
-d "payment_method_options[card][network_token][electronic_commerce_indicator]=07"
{
"id": "pi_3MtwBwLkdIwHu7ix28a3tqPa",
"status": "succeeded",
"object": "payment_intent",
"payment_method": "pm_1QaKJmG9VRQ2700S5JcsJv4Q",
"latest_charge": {
"payment_method_details": {
"card": {
"network_transaction_id": "8723dfab9c4e56a1b8cd0291ef67ac43"
...
}
...
}
...
}
...
}
In Adyen, you can charge with a Network Token by calling the /payments API.
curl 'https://checkout-test.adyen.com/v71/payments' \
-X 'POST' \
-H 'X-API-Key: <ADYEN_API_KEY>' \
-H 'Content-Type: application/json' \
--data '{
"amount": {
"value": 1000,
"currency": "USD"
},
"paymentMethod": {
"type": "networkToken",
"brand": "visa",
"number": "4446878164352273",
"expiryMonth": "11",
"expiryYear": "2030"
},
"mpiData": {
"directoryResponse": "Y",
"authenticationResponse": "Y",
"tokenAuthenticationVerificationValue": "5hgXjWJBDkiONPQ6EFxtgA==",
"eci": "07"
},
"reference": "YourTransactionReference",
"shopperReference": "YourShopperId",
"shopperInteraction": "Ecommerce",
"recurringProcessingModel": "CardOnFile",
"merchantAccount": "YourMerchantAccount"
}'
{
"pspReference": "CNX333SGC7ZN6KV5",
"merchantReference": "YourTransactionReference",
"resultCode": "Authorised",
"amount": {
"currency": "USD",
"value": 1000
},
"additionalData": {
"networkTxReference": "377216678422238",
...
},
...
}
In Braintree, you can charge with a Network Token by following the Bring Your Own Token (BYOT) flow. First, tokenize the Network Token to obtain a single-use paymentMethod.id, then pass that id into a chargeCreditCard mutation as described in Braintree's BYOT guide.
curl 'https://payments.sandbox.braintree-api.com/graphql' \
-X 'POST' \
-H 'Authorization: Basic <BRAINTREE_BASIC_AUTH>' \
-H 'Braintree-Version: 2019-01-01' \
-H 'Content-Type: application/json' \
--data '{
"query": "mutation TokenizeNetworkToken($input: TokenizeNetworkTokenInput!) { tokenizeNetworkToken(input: $input) { paymentMethod { id usage } } }",
"variables": {
"input": {
"networkToken": {
"number": "4446878164352273",
"expirationMonth": 11,
"expirationYear": 2030,
"cryptogram": "5hgXjWJBDkiONPQ6EFxtgA==",
"originDetails": {
"origin": "NETWORK_TOKEN",
"sourceCardLast4": "2273"
}
}
}
}
}'
{
"data": {
"tokenizeNetworkToken": {
"paymentMethod": {
"id": "tokencc_bc_abc123_xyz",
"usage": "SINGLE_USE"
}
}
}
}
Save the Network Transaction identifier returned by the processor during the initial transaction. This allows you to seamlessly charge the card with new processors or acquirers in the future and achieve the best approval rates. Refer to your current processor’s documentation or support for guidance on accessing this value.
Subsequent Charges
After the initial customer-initiated transaction, merchants can process merchant-initiated transactions (MITs) using the stored Network Token for follow-on charges such as subscriptions, renewals, or delayed captures. These transactions rely on stored-credential indicators and the original customer mandate, and typically do not require generating a new cryptogram (though some processors, such as Adyen, may still expect cryptogram fields in the request).
- Checkout.com
- Stripe
- Adyen
- Braintree
In Checkout.com, you can charge with a Network Token by calling the /payments API.
Pass the Network Transaction identifier of the first charge into the previous_payment_id field.
curl 'https://api.sandbox.checkout.com/payments' \
-X 'POST' \
-H 'Authorization: Bearer <CHECKOUT_SECRET_KEY>' \
-H 'Content-Type: application/json' \
--data '{
"source": {
"type": "network_token",
"token_type": "vts",
"number": "4446878164352273",
"expiry_month": "11",
"expiry_year": "2030",
"stored": true
},
"amount": 1000,
"currency": "USD",
"payment_type": "Recurring",
"merchant_initiated": true,
"processing_channel_id": "pc_svyx12xvaaduraq3ckb2gwnvzq",
"previous_payment_id": "<first_network_transaction_identifier>"
}'
{
"id": "pay_myvj6zxrt45edgrswrliyxbtnq",
"approved": true,
"status": "Authorized",
"amount": 1000,
"currency": "USD",
"source": {
"type": "card",
"id": "src_gguer4xlawke3ctljczft7573a",
...
},
"scheme_id": "647537243670773",
...
}
In Stripe, you can charge with a Network Token by creating a Payment Intent.
Pass the Network Transaction identifier of the first charge into the payment_method_options[card][mit_exemption][network_transaction_id] field.
curl https://api.stripe.com/v1/payment_intents \
-H "Authorization: Bearer <STRIPE_SECRET_KEY>" \
-d "amount=1000" \
-d "currency=usd" \
-d "confirm=true" \
-d "payment_method_types[]=card" \
-d "payment_method_data[type]=card" \
-d "payment_method_data[card][exp_month]=11" \
-d "payment_method_data[card][exp_year]=2030" \
-d "payment_method_data[card][last4]=2273" \
-d "payment_method_data[card][network_token][number]=4446878164352273" \
-d "payment_method_data[card][network_token][exp_month]=11" \
-d "payment_method_data[card][network_token][exp_year]=2030" \
-d "payment_method_options[card][mit_exemption][network_transaction_id]=<first_network_transaction_identifier>"
{
"id": "pi_3MtwBwLkdIwHu7ix28a3tqPa",
"status": "succeeded",
"object": "payment_intent",
"payment_method": "pm_1QaKJmG9VRQ2700S5JcsJv4Q",
"latest_charge": {
"payment_method_details": {
"card": {
"network_transaction_id": "8723dfab9c4e56a1b8cd0291ef67ac43"
...
}
...
}
...
}
...
}
In Adyen, you can charge with a Network Token by calling the /payments API.
curl 'https://checkout-test.adyen.com/v71/payments' \
-X 'POST' \
-H 'X-API-Key: <ADYEN_API_KEY>' \
-H 'Content-Type: application/json' \
--data '{
"amount": {
"value": 1000,
"currency": "USD"
},
"paymentMethod": {
"type": "networkToken",
"brand": "visa",
"number": "4446878164352273",
"expiryMonth": "11",
"expiryYear": "2030"
},
"mpiData": {
"directoryResponse": "Y",
"authenticationResponse": "Y",
"tokenAuthenticationVerificationValue": "5hgXjWJBDkiONPQ6EFxtgA==",
"eci": "07"
},
"additionalData": {
"networkTxReference": "<first_network_transaction_identifier>"
},
"reference": "YourTransactionReference",
"shopperReference": "YourShopperId",
"shopperInteraction": "Ecommerce",
"recurringProcessingModel": "CardOnFile",
"merchantAccount": "YourMerchantAccount"
}'
{
"pspReference": "CNX333SGC7ZN6KV5",
"merchantReference": "YourTransactionReference",
"resultCode": "Authorised",
"amount": {
"currency": "USD",
"value": 1000
},
"additionalData": {
"networkTxReference": "377216678422238",
...
},
...
}
In Braintree, you can charge with a Network Token using the Bring Your Own Token (BYOT) flow. For merchant-initiated transactions, omit the cryptogram when tokenizing, then pass the prior network transaction reference into options.externalVault.verifyingNetworkTransactionId and set transaction.paymentInitiator on the chargeCreditCard mutation as described in Braintree's BYOT guide.
curl 'https://payments.sandbox.braintree-api.com/graphql' \
-X 'POST' \
-H 'Authorization: Basic <BRAINTREE_BASIC_AUTH>' \
-H 'Braintree-Version: 2019-01-01' \
-H 'Content-Type: application/json' \
--data '{
"query": "mutation TokenizeNetworkToken($input: TokenizeNetworkTokenInput!) { tokenizeNetworkToken(input: $input) { paymentMethod { id usage } } }",
"variables": {
"input": {
"networkToken": {
"number": "4446878164352273",
"expirationMonth": 11,
"expirationYear": 2030,
"originDetails": {
"origin": "NETWORK_TOKEN",
"sourceCardLast4": "2273"
}
}
}
}
}'
{
"data": {
"tokenizeNetworkToken": {
"paymentMethod": {
"id": "tokencc_bc_abc123_xyz",
"usage": "SINGLE_USE"
}
}
}
}