Detokenization
Detokenization refers to the process by which non-sensitive token identifiers are replaced with the original token data represented by those tokens. Basis Theory supports detokenization through the use of expressions within our serverless Reactor platform and the Proxy.
Detokenization is performed whenever a detokenization expression is identified within a request.
In their simplest form, these are Liquid objects of the form {{ token: <tokenId> }}
.
This expression will be replaced with the token with ID <tokenId>
.
Any string
value contained in a detokenization expression is expected to represent the id
of a token within your tenant.
If no such token is found with that identifier, the request will be rejected with a 400 error.
If a token is found with the given identifier, but the calling application is missing permission to use that token, then the request will be rejected with a 403 error. See the following links for more information about the permissions required to use tokens in a Reactor or Proxy.
Token data can also be transformed before including it in a request by applying filters within an expression. In general, filters are applied during detokenization using one of the following syntaxes:
- Full Token Expressions
{{ token: <tokenId> | <filter1> | <filter2> | ... }}
- Filters able to compute on any token property
- Data only Expressions
{{ <tokenId> | <filter1> | <filter2> | ... }}
- Filters only able to compute on data
- Token Intent Expressions
{{ token_intent: <tokenIntentId> | <filter1> | <filter2> | ... }}
- Filters able to compute on any token intent property
Check out the detokenization examples below to see some examples of filters in action.
Detokenizing Primitive Data
Tokens containing primitive data values, such as a single string or numeric value, can be easily detokenized within a request just by substituting the token data in place of the detokenization expression. For example, say you have the token:
{
"id": "1d08babf-456a-4bef-993d-aece3c1a2f66",
"type": "social_security_number",
"data": "111-22-3333"
}
Then a request containing the detokenization expression:
{
"ssn": "{{ token: 1d08babf-456a-4bef-993d-aece3c1a2f66 | json: '$.data' }}"
}
will be detokenized into the request:
{
"ssn": "111-22-3333"
}
Detokenizing Complex Data
Card, Bank, or generic tokens containing complex JSON objects can all be detokenized as well.
Detokenization of complex token data is performed by embedding the token's JSON data within the request.
For example, say you have the card
token:
{
"id": "5c20545b-52dc-4a60-b9b5-5b7c84f22369",
"type": "card",
"data": {
"number": "4242424242424242",
"expiration_month": 12,
"expiration_year": 2025,
"cvc": "123"
}
}
Then you can embed the entire Card object within a request with the detokenization expression:
{
"card": "{{ token: 5c20545b-52dc-4a60-b9b5-5b7c84f22369 | json: '$.data' }}"
}
which will be detokenized into the request:
{
"card": {
"number": "4242424242424242",
"expiration_month": 12,
"expiration_year": 2025,
"cvc": "123"
}
}
Using Token Properties
In addition to the token's data, all properties of a token can be used within Detokenization Expressions.
For example, say you have the card
token:
{
"id": "5c20545b-52dc-4a60-b9b5-5b7c84f22369",
"type": "card",
"data": {
"number": "4242424242424242",
"expiration_month": 12,
"expiration_year": 2025,
"cvc": "123"
},
"metadata": {
"name": "John Doe"
},
"enrichments": {
"bin_details": {
// ...
"card_brand": "Visa"
// ...
}
}
}
The following example shows how to use the card_brand
from the BIN Details Enrichment and the name
from metadata
:
{
"card_brand": "{{ token: 5c20545b-52dc-4a60-b9b5-5b7c84f22369 | json: '$.enrichments.bin_details.card_brand' }}",
"card_holder_name": "{{ token: 5c20545b-52dc-4a60-b9b5-5b7c84f22369 | json: '$.metadata.name' }}"
}
which will be detokenized into the request:
{
"card_brand": "Visa",
"card_holder_name": "John Doe"
}
Data-only Expressions
These expressions are an ease of use addition to our Expression language, making it simpler to get access to just the token data instead of the full token results. Here are a few examples:
The following expressions are equivalent:
{{ 815029c2-29ec-4fc2-8cd4-99feb3ee582c }}
{{ token: 815029c2-29ec-4fc2-8cd4-99feb3ee582c | json: '$.data' }}
{{ 815029c2-29ec-4fc2-8cd4-99feb3ee582c | json: '$.number' }}
{{ token: 815029c2-29ec-4fc2-8cd4-99feb3ee582c | json: '$.data.number' }}
Examples
Each of the use cases shown below include an example for both Reactors and the Proxy - select the appropriate tab at the top of the right-hand column to choose which examples to show.
Prerequisites
- Proxy
- Reactor
For the examples below, we will proxy requests to Spreedly using their Payment Methods API.
This is the same request that is being made from within the Spreedly - Card
Reactor.
This API endpoint accepts POST requests with a body of the form:
{
"payment_method": {
"credit_card": {
"number": "4242424242424242",
"month": "12",
"year": "2025",
"verification_value": "123",
"full_name": "Card Owner"
},
"retained": true
}
}
and it authenticates using basic auth -
we will be passing a simulated value in the Authorization
header on the example proxy requests -
replace this with your own authentication credentials if you want to follow along.
For the examples below, we will be using a Reactor created using the Spreedly - Card
Reactor Template available in the customer portal.
We will need the id
of this Reactor in the examples below: d08bc998-9301-495c-a2e5-04f8dc0916b4
.
This Reactor accepts the following request parameters:
name | type | optional |
---|---|---|
card.number | string | false |
card.expiration_month | number | false |
card.expiration_year | number | false |
card.cvc | string | true |
card_owner_full_name | string | false |
Use Complex Tokens
In this example, we show how you can use a Card token to create a Spreedly payment method.
Say you have created the card
token:
{
"id": "815029c2-29ec-4fc2-8cd4-99feb3ee582c",
"type": "card",
"data": {
"number": "4242424242424242",
"expiration_month": 11,
"expiration_year": 2025,
"cvc": "123"
}
}
We have not tokenized the card owner's name, John Doe
. Assume that we have this plaintext value directly available to our application to pass into the Reactor or Proxy request.
Original Request
- Proxy
- Reactor
curl "https://api.basistheory.com/proxy" \
-H "BT-API-KEY: <PRIVATE_API_KEY>" \
-H "BT-PROXY-URL: https://core.spreedly.com/v1/payment_methods.json" \
-H "Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" \
-H "Content-Type: application/json" \
-X "POST" \
-d '{
"payment_method": {
"credit_card": {
"number": "{{ token: 815029c2-29ec-4fc2-8cd4-99feb3ee582c | json: '$.data.number'}}",
"month": "{{ token: 815029c2-29ec-4fc2-8cd4-99feb3ee582c | json: '$.data.expiration_month'}}",
"year": "{{ token: 815029c2-29ec-4fc2-8cd4-99feb3ee582c | json: '$.data.expiration_year'}}",
"verification_value": "{{ token: 815029c2-29ec-4fc2-8cd4-99feb3ee582c | json: '$.data.cvc'}}",
"full_name": "John Doe"
},
"retained": true
}
}'
curl "https://api.basistheory.com/reactors/d08bc998-9301-495c-a2e5-04f8dc0916b4/react" \
-H "BT-API-KEY: <PRIVATE_API_KEY>" \
-H "Content-Type: application/json" \
-X "POST" \
-d '{
"args": {
"card": "{{ token: 815029c2-29ec-4fc2-8cd4-99feb3ee582c | json: '$.data'}}",
"card_owner_full_name": "John Doe"
}
}'
Detokenized Request
- Proxy
- Reactor
{
"payment_method": {
"credit_card": {
"number": "4242424242424242",
"month": "11",
"year": "2025",
"verification_value": "123",
"full_name": "John Doe"
},
"retained": true
}
}
{
"args": {
"card": {
"number": "4242424242424242",
"expiration_month": 11,
"expiration_year": 2025,
"cvc": "123"
},
"card_owner_full_name": "John Doe"
},
"configuration": {...}
}
Override a Field on a Token
In this example, we show how you can use a Card token to create a Spreedly payment method,
but provide an updated CVC (987
) that is different from the cvc
value stored within the token.
This could be desired if the updated CVC was collected directly from a user interface, possibly as a challenge to the user to prove they own the card.
Here, we will be using the same card
token and card owner name from the previous Use Complex Tokens example.
Original Request
- Proxy
- Reactor
curl "https://api.basistheory.com/proxy" \
-H "BT-API-KEY: <PRIVATE_API_KEY>" \
-H "BT-PROXY-URL: https://core.spreedly.com/v1/payment_methods.json" \
-H "Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" \
-H "Content-Type: application/json" \
-X "POST" \
-d '{
"payment_method": {
"credit_card": {
"number": "{{ token: 815029c2-29ec-4fc2-8cd4-99feb3ee582c | json: '$.data.number'}}"
"month": "{{ token: 815029c2-29ec-4fc2-8cd4-99feb3ee582c | json: '$.data.expiration_month'}}",
"year": "{{ token: 815029c2-29ec-4fc2-8cd4-99feb3ee582c | json: '$.data.expiration_year'}}",
"verification_value": "987",
"full_name": "John Doe"
},
"retained": true
}
}'
curl "https://api.basistheory.com/reactors/d08bc998-9301-495c-a2e5-04f8dc0916b4/react" \
-H "BT-API-KEY: <PRIVATE_API_KEY>" \
-H "Content-Type: application/json" \
-X "POST" \
-d '{
"args": {
"card": {
"number": "{{ token: 815029c2-29ec-4fc2-8cd4-99feb3ee582c | json: '$.data.number'}}",
"expiration_month": "{{ token: 815029c2-29ec-4fc2-8cd4-99feb3ee582c | json: '$.data.expiration_month'}}",
"expiration_year": "{{ token: 815029c2-29ec-4fc2-8cd4-99feb3ee582c | json: '$.data.expiration_year'}}",
"cvc": "987"
},
"card_owner_full_name": "John Doe"
}
}'
Detokenized Request
- Proxy
- Reactor
{
"payment_method": {
"credit_card": {
"number": "4242424242424242",
"month": "11",
"year": "2025",
"verification_value": "987",
"full_name": "John Doe"
},
"retained": true
}
}
{
"args": {
"card": {
"number": "4242424242424242",
"expiration_month": 11,
"expiration_year": 2025,
"cvc": "987"
},
"card_owner_full_name": "John Doe"
},
"configuration": {...}
}
Use Multiple Tokens
In this example, we will show how you can use multiples tokens - a Card Number token and a
generic PII
token to hold the card owner's name:
{
"id": "d9939ddc-d7be-423b-a0f5-69f65fec57df",
"type": "card_number",
"data": "5555555555554444",
"containers": ["/pci/high/"]
}
{
"id": "f4d86311-1254-4155-b532-b651279a8cc0",
"type": "token",
"data": "Jane Doe",
"containers": ["/pii/moderate/"]
}
In this example, we have not tokenized the card's expiration date or CVC - say our application accepts these values in plaintext and forwards them directly into the request.
Original Request
- Proxy
- Reactor
curl "https://api.basistheory.com/proxy" \
-H "BT-API-KEY: <PRIVATE_API_KEY>" \
-H "BT-PROXY-URL: https://core.spreedly.com/v1/payment_methods.json" \
-H "Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" \
-H "Content-Type: application/json" \
-X "POST" \
-d '{
"payment_method": {
"credit_card": {
"number": "{{ token: d9939ddc-d7be-423b-a0f5-69f65fec57df | json: '$.data' }}",
"month": "10",
"year": "2024",
"verification_value": "789",
"full_name": "{{ token: f4d86311-1254-4155-b532-b651279a8cc0 | json: '$.data' }}"
},
"retained": true
}
}'
curl "https://api.basistheory.com/reactors/d08bc998-9301-495c-a2e5-04f8dc0916b4/react" \
-H "BT-API-KEY: <PRIVATE_API_KEY>" \
-H "Content-Type: application/json" \
-X "POST" \
-d '{
"args": {
"card": {
"number": "{{ token: d9939ddc-d7be-423b-a0f5-69f65fec57df | json: '$.data'}}"
"expiration_month": 10,
"expiration_year": 2024,
"cvc": "789"
},
"card_owner_full_name": "{{ token: f4d86311-1254-4155-b532-b651279a8cc0 | json: '$.data'}}"
}
}'
Detokenized Request
- Proxy
- Reactor
{
"payment_method": {
"credit_card": {
"number": "5555555555554444",
"month": "10",
"year": "2024",
"verification_value": "789",
"full_name": "Jane Doe"
},
"retained": true
}
}
{
"args": {
"card": {
"number": "5555555555554444",
"expiration_month": 10,
"expiration_year": 2024,
"cvc": "789"
},
"card_owner_full_name": "Jane Doe"
},
"configuration": {...}
}
Combine Multiple Tokens within a Single Argument
In this example, we will show how you can combine the data from multiple tokens within a single field on a request. Say we have chosen to store the card holder's first and last names as separate tokens:
{
"id": "523949a9-e32f-4b5b-a0ad-7a435c79deb4",
"type": "token",
"data": "John"
}
{
"id": "42af9170-e6ca-4ea7-a43b-730a0b47b6d0",
"type": "token",
"data": "Brown"
}
Also, we have the card
token:
{
"id": "b78b4bee-5499-42dd-8671-f1d23d32355b",
"type": "card",
"data": {
"number": "5105105105105100",
"expiration_month": 5,
"expiration_year": 2025,
"cvc": "123"
}
}
Then we can concatenate the first and last name tokens within the request as shown in the sample requests.
Original Request
- Proxy
- Reactor
curl "https://api.basistheory.com/proxy" \
-H "BT-API-KEY: <PRIVATE_API_KEY>" \
-H "BT-PROXY-URL: https://core.spreedly.com/v1/payment_methods.json" \
-H "Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" \
-H "Content-Type: application/json" \
-X "POST" \
-d '{
"payment_method": {
"credit_card": {
"number": "{{token: b78b4bee-5499-42dd-8671-f1d23d32355b | json: '$.data.number'}}",
"month": "{{token: b78b4bee-5499-42dd-8671-f1d23d32355b | json: '$.data.expiration_month'}}",
"year": "{{token: b78b4bee-5499-42dd-8671-f1d23d32355b | json: '$.data.expiration_year'}}",
"verification_value": "{{token: b78b4bee-5499-42dd-8671-f1d23d32355b | json: '$.data.cvc'}}",
"full_name": "{{523949a9-e32f-4b5b-a0ad-7a435c79deb4}} {{42af9170-e6ca-4ea7-a43b-730a0b47b6d0}}" // Data only expression
},
"retained": true
}
}'
curl "https://api.basistheory.com/reactors/d08bc998-9301-495c-a2e5-04f8dc0916b4/react" \
-H "BT-API-KEY: <PRIVATE_API_KEY>" \
-H "Content-Type: application/json" \
-X "POST" \
-d '{
"args": {
"card": "{{ token: b78b4bee-5499-42dd-8671-f1d23d32355b | json: '$.data'}}",
"card_owner_full_name": "{{523949a9-e32f-4b5b-a0ad-7a435c79deb4}} {{42af9170-e6ca-4ea7-a43b-730a0b47b6d0}}" // Data only expression
}
}'
Detokenized Request
- Proxy
- Reactor
{
"payment_method": {
"credit_card": {
"number": "5105105105105100",
"month": "5",
"year": "2025",
"verification_value": "123",
"full_name": "John Brown"
},
"retained": true
}
}
{
"args": {
"card": {
"number": "5105105105105100",
"expiration_month": 5,
"expiration_year": 2025,
"cvc": "123"
},
"card_owner_full_name": "John Brown"
},
"configuration": {...}
}
Using Custom Token Schemas
In this example, we will store our card data within a custom generic token that contains additional fields relevant to our application:
{
"id": "9a48a051-972b-4569-8fd5-cbe17a604f96",
"type": "token",
"data": {
"card": {
"card_number": "4000056655665556",
"exp_month": 4,
"exp_year": 2026,
"owner": {
"first_name": "John",
"middle_name": "Andrew",
"last_name": "Smith"
}
},
"billing_address": {
"street_address": "175 5th Ave",
"city": "New York",
"state": "NY",
"zip": "10010"
}
},
"containers": ["/pci/high/"]
}
Notice that the card owner's full name is constructed by concatenating the card.owner.first_name
and card.owner.last_name
properties into a single string value.
Original Request
- Proxy
- Reactor
curl "https://api.basistheory.com/proxy" \
-H "BT-API-KEY: <PRIVATE_API_KEY>" \
-H "BT-PROXY-URL: https://core.spreedly.com/v1/payment_methods.json" \
-H "Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" \
-H "Content-Type: application/json" \
-X "POST" \
-d '{
"payment_method": {
"credit_card": {
"number": "{{token: 9a48a051-972b-4569-8fd5-cbe17a604f96 | json: '$.data.card.card_number'}}",
"month": "{{token: 9a48a051-972b-4569-8fd5-cbe17a604f96 | json: '$.data.card.exp_month'}}",
"year": "{{token: 9a48a051-972b-4569-8fd5-cbe17a604f96 | json: '$.data.card.exp_year'}}",
"full_name": "{{token: 9a48a051-972b-4569-8fd5-cbe17a604f96 | json: '$.data.card.owner.first_name'}} {{token: 9a48a051-972b-4569-8fd5-cbe17a604f96 | json: '$.data.card.owner.last_name'}}"
},
"retained": true
}
}'
curl "https://api.basistheory.com/reactors/d08bc998-9301-495c-a2e5-04f8dc0916b4/react" \
-H "BT-API-KEY: <PRIVATE_API_KEY>" \
-H "Content-Type: application/json" \
-X "POST" \
-d '{
"args": {
"card": {
"number": "{{token: 9a48a051-972b-4569-8fd5-cbe17a604f96 | json: '$.data.card.card_number'}}",
"expiration_month": "{{token: 9a48a051-972b-4569-8fd5-cbe17a604f96 | json: '$.data.card.exp_month'}}",
"expiration_year": "{{token: 9a48a051-972b-4569-8fd5-cbe17a604f96 | json: '$.data.card.exp_year'}}",
},
"card_owner_full_name": "{{token: 9a48a051-972b-4569-8fd5-cbe17a604f96 | json: '$.data.card.owner.first_name'}} {{token: 9a48a051-972b-4569-8fd5-cbe17a604f96 | json: '$.data.card.owner.last_name'}}"
}
}'
Detokenized Request
- Proxy
- Reactor
{
"payment_method": {
"credit_card": {
"number": "4000056655665556",
"month": "4",
"year": "2026",
"full_name": "John Smith"
},
"retained": true
}
}
{
"args": {
"card": {
"number": "4000056655665556",
"expiration_month": 4,
"expiration_year": 2026,
},
"card_owner_full_name": "John Smith"
},
"configuration": {...}
}
Transforming Token Data
In this example, we will be using the card
token:
{
"id": "67d76cd0-bc2a-4a73-be8c-6539c81db465",
"type": "card",
"data": {
"number": "4242424242424242",
"expiration_month": 3,
"expiration_year": 2025,
"cvc": "123"
}
}
We will transform the expiration_month
property within this token to be padded on the left with a 0
to two characters,
and to only provide the last two digits of the expiration year.
Original Request
- Proxy
- Reactor
curl "https://api.basistheory.com/proxy" \
-H "BT-API-KEY: <PRIVATE_API_KEY>" \
-H "BT-PROXY-URL: https://core.spreedly.com/v1/payment_methods.json" \
-H "Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" \
-H "Content-Type: application/json" \
-X "POST" \
-d '{
"payment_method": {
"credit_card": {
"number": "{{token: 67d76cd0-bc2a-4a73-be8c-6539c81db465 | json: '$.data.card_number'}}",
"month": "{{token: 67d76cd0-bc2a-4a73-be8c-6539c81db465 | json: '$.data.expiration_month' | pad_left: 2, '0'}}",
"year": "{{token: 67d76cd0-bc2a-4a73-be8c-6539c81db465 | json: '$.data.expiration_year' | to_string | slice: -2, 2}}",
"full_name": "John Doe"
},
"retained": true
}
}'
curl "https://api.basistheory.com/reactors/d08bc998-9301-495c-a2e5-04f8dc0916b4/react" \
-H "BT-API-KEY: <PRIVATE_API_KEY>" \
-H "Content-Type: application/json" \
-X "POST" \
-d '{
"args": {
"card": {
"number": "{{token: 67d76cd0-bc2a-4a73-be8c-6539c81db465 | json: '$.data.card_number'}}",
"expiration_month": "{{token: 67d76cd0-bc2a-4a73-be8c-6539c81db465 | json: '$.data.expiration_month' | pad_left: 2, '0'}}",
"expiration_year": "{{token: 67d76cd0-bc2a-4a73-be8c-6539c81db465 | json: '$.data.expiration_year' | to_string | slice: -2, 2}}",
},
"card_owner_full_name": "John Doe"
}
}'
Detokenized Request
- Proxy
- Reactor
{
"payment_method": {
"credit_card": {
"number": "4000056655665556",
"month": "03",
"year": "25",
"full_name": "John Doe"
},
"retained": true
}
}
{
"args": {
"card": {
"number": "4000056655665556",
"expiration_month": "03",
"expiration_year": "25"
},
"card_owner_full_name": "John Smith"
},
"configuration": {...}
}