NAV
Reactor Proxy

Overview

Reference

More

Introduction

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 our serverless Reactor platform and the Proxy.

Detokenization Expressions

Detokenization is performed whenever a detokenization expression is identified within a request. In their simplest form, these are expressions of the form {{<tokenId>}} which will be replaced with the token data contained within the token with id <tokenId>.

Any pattern that is wrapped in {{ }} will be evaluated for detokenization. Detokenization expressions containing non-uuid values, such as {{non uuid value}} will be ignored and the expression will not be replaced within the request.

Any uuid 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 uuid 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 within a detokenization expression before including it in a request, for example, to project a single primitive value out of a complex object or to format a value. In general, transformations are applied to token data using the syntax:
{{ <tokenId> | <transformation> }}

Check out the section on Transformations below for more background information and detailed documentation about all supported transformation functions. Also, check out the examples below to see transformations 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": "{{1d08babf-456a-4bef-993d-aece3c1a2f66}}"
}

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": "{{5c20545b-52dc-4a60-b9b5-5b7c84f22369}}"
}

which will be detokenized into the request:

{
  "card": {
    "number": "4242424242424242",
    "expiration_month": 12,
    "expiration_year": 2025,
    "cvc": "123"
  }
}

Transformations

Oftentimes data may be tokenized in one format, but you wish to use this data in a different format within a request. For example, several pieces of data may be stored together in a complex object such as a Card token, but you wish to only use a single piece of that data within a request.

For this purpose, we offer the ability to apply transformation functions to a detokenization expression. A token can be transformed by specifying a transformation expression after the | symbol:
{{ <tokenId> | <transformation> }}

You can effectively think of this as "piping" the token data into the transformation function in the same way you may be familiar with the "pipe" operator from Unix-like systems. Multiple transformations can be chained together by "piping" the result of each function into the next, applying in order from left to right:
{{ <tokenId> | <transformation1> | <transformation2> | ... }}

The following sections document the supported library of transformation functions.

JSON Path

Complex data is typically tokenized as a JSON object within a token's data. In order to facilitate transformations on this JSON data, arbitrary JSON Path expressions (proposed spec) can be applied to a token's data:
{{<tokenId> | <jsonPathExpression>}}

All standard JSON Path syntax is supported, provided that the expression resolves to a single value. If the expression resolves to multiple values, the request will result in a 400 error. If the expression does not resolve to any value, then the expression will resolve to the value null.

JSON Path Examples

Given a token with the data:

{
  "id": "d35412f4-9d3b-45d8-b051-fe4b7d4e14c5",
  "type": "token",
  "data": { 
    "book": [
      { 
        "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      { 
        "category": "fantasy",
        "author": "J. R. R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  }
}
Expression Result
{{d35412f4-9d3b-45d8-b051-fe4b7d4e14c5 | $.bicycle.color}} "red"
{{d35412f4-9d3b-45d8-b051-fe4b7d4e14c5 | $.bicycle}} { "color": "red", "price": 19.95 }
{{d35412f4-9d3b-45d8-b051-fe4b7d4e14c5 | $.book[0].author}} "Herman Melville"
{{d35412f4-9d3b-45d8-b051-fe4b7d4e14c5 | $.book[?(@.price < 10)].title}} "Moby Dick"
{{d35412f4-9d3b-45d8-b051-fe4b7d4e14c5 | $.nonexistent}} null
{{d35412f4-9d3b-45d8-b051-fe4b7d4e14c5 | $.book..author}} <400 Error>

Examples

Based on your use cases, you may prefer to store your sensitive data with Basis Theory using one of our pre-defined Token Types or even use your own custom data schema within a schemaless generic token. However you choose to tokenize your sensitive data, Basis Theory grants you the flexibility to use this data within our serverless Reactor platform or the Proxy.

In this section, we will walk through several examples of how you can detokenize data within Reactors or Proxy requests regardless of how the data was tokenized.

Prerequisites

If Reactors or Proxy are completely new to you, we recommend you first read our Reactors or Proxy concept pages.

Reactor Prerequisites

For the examples below, we will be using a Reactor created using the Spreedly - Card Reactor Formula. We will need the id of this Reactor in the examples below: d08bc998-9301-495c-a2e5-04f8dc0916b4.

This Reactor Formula 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

Proxy Prerequisites

Also in the examples below, we will be proxying requests to Spreedly using their Payment Methods API. This is the same request that is being made from within the Spreedly - Card Reactor Formula.

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.

Use Complex Tokens

Original Request

curl "https://api.basistheory.com/reactors/d08bc998-9301-495c-a2e5-04f8dc0916b4/react" \
      -H "BT-API-KEY: key_NS21v84n7epsSc5WzoFjM6" \
      -H "Content-Type: application/json" \
      -X "POST" \
      -d '{
            "args": {
              "card": "{{815029c2-29ec-4fc2-8cd4-99feb3ee582c}}",
              "card_owner_full_name": "John Doe"
            }
        }'
curl "https://api.basistheory.com/proxy" \
      -H "BT-API-KEY: key_NS21v84n7epsSc5WzoFjM6" \
      -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": "{{815029c2-29ec-4fc2-8cd4-99feb3ee582c | $.number}}",
                "month": "{{815029c2-29ec-4fc2-8cd4-99feb3ee582c | $.expiration_month}}",
                "year": "{{815029c2-29ec-4fc2-8cd4-99feb3ee582c | $.expiration_year}}",
                "verification_value": "{{815029c2-29ec-4fc2-8cd4-99feb3ee582c | $.cvc}}",
                "full_name": "John Doe"
              },
              "retained": true
            }
          }'

Detokenized Request

{
  "args": {
    "card": {
      "number": "4242424242424242",
      "expiration_month": 11,
      "expiration_year": 2025,
      "cvc": "123"
    },
    "card_owner_full_name": "John Doe"
  }, 
  "configuration": {...}
}
{
  "payment_method": {
    "credit_card": {
      "number": "4242424242424242",
      "month": "11",
      "year": "2025",
      "verification_value": "123",
      "full_name": "John Doe"
    },
    "retained": true
  }
}

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.

Override a Field on a Token

Original Request

curl "https://api.basistheory.com/reactors/d08bc998-9301-495c-a2e5-04f8dc0916b4/react" \
      -H "BT-API-KEY: key_NS21v84n7epsSc5WzoFjM6" \
      -H "Content-Type: application/json" \
      -X "POST" \
      -d '{
            "args": {
              "card": {
                "number": "{{815029c2-29ec-4fc2-8cd4-99feb3ee582c | $.number}}",
                "expiration_month": "{{815029c2-29ec-4fc2-8cd4-99feb3ee582c | $.expiration_month}}",
                "expiration_year": "{{815029c2-29ec-4fc2-8cd4-99feb3ee582c | $.expiration_year}}",
                "cvc": "987"
              },
              "card_owner_full_name": "John Doe"
            }
        }'
curl "https://api.basistheory.com/proxy" \
      -H "BT-API-KEY: key_NS21v84n7epsSc5WzoFjM6" \
      -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": "{{815029c2-29ec-4fc2-8cd4-99feb3ee582c | $.number}}"
                "month": "{{815029c2-29ec-4fc2-8cd4-99feb3ee582c | $.expiration_month}}",
                "year": "{{815029c2-29ec-4fc2-8cd4-99feb3ee582c | $.expiration_year}}",
                "verification_value": "987",
                "full_name": "John Doe"
              },
              "retained": true
            }
          }'

Detokenized Request

{
  "args": {
    "card": {
      "number": "4242424242424242",
      "expiration_month": 11,
      "expiration_year": 2025,
      "cvc": "987"
    },
    "card_owner_full_name": "John Doe"
  }, 
  "configuration": {...}
}
{
  "payment_method": {
    "credit_card": {
      "number": "4242424242424242",
      "month": "11",
      "year": "2025",
      "verification_value": "987",
      "full_name": "John Doe"
    },
    "retained": true
  }
}

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.

Use Multiple Tokens

Original Request

curl "https://api.basistheory.com/reactors/d08bc998-9301-495c-a2e5-04f8dc0916b4/react" \
      -H "BT-API-KEY: key_NS21v84n7epsSc5WzoFjM6" \
      -H "Content-Type: application/json" \
      -X "POST" \
      -d '{
            "args": {
              "card": {
                "number": "{{d9939ddc-d7be-423b-a0f5-69f65fec57df}}"
                "expiration_month": 10,
                "expiration_year": 2024,
                "cvc": "789"
              },
              "card_owner_full_name": "{{f4d86311-1254-4155-b532-b651279a8cc0}}"
            }
        }'
curl "https://api.basistheory.com/proxy" \
      -H "BT-API-KEY: key_NS21v84n7epsSc5WzoFjM6" \
      -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": "{{d9939ddc-d7be-423b-a0f5-69f65fec57df}}",
                "month": "10",
                "year": "2024",
                "verification_value": "789",
                "full_name": "{{f4d86311-1254-4155-b532-b651279a8cc0}}"
              },
              "retained": true
            }
          }'

Detokenized Request

{
  "args": {
    "card": {
      "number": "5555555555554444",
      "expiration_month": 10,
      "expiration_year": 2024,
      "cvc": "789"
    },
    "card_owner_full_name": "Jane Doe"
  }, 
  "configuration": {...}
}
{
  "payment_method": {
    "credit_card": {
      "number": "5555555555554444",
      "month": "10",
      "year": "2024",
      "verification_value": "789",
      "full_name": "Jane Doe"
    },
    "retained": true
  }
}

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",
  "privacy": {
    "classification": "pci",
    "impact_level": "high"
  }
}
{
  "id": "f4d86311-1254-4155-b532-b651279a8cc0",
  "type": "token",
  "data": "Jane Doe",
  "privacy": {
    "classification": "pii",
    "impact_level": "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.

Combine Multiple Tokens within a Single Argument

Original Request

curl "https://api.basistheory.com/reactors/d08bc998-9301-495c-a2e5-04f8dc0916b4/react" \
      -H "BT-API-KEY: key_NS21v84n7epsSc5WzoFjM6" \
      -H "Content-Type: application/json" \
      -X "POST" \
      -d '{
            "args": {
              "card": "{{b78b4bee-5499-42dd-8671-f1d23d32355b}}",
              "card_owner_full_name": "{{523949a9-e32f-4b5b-a0ad-7a435c79deb4}} {{42af9170-e6ca-4ea7-a43b-730a0b47b6d0}}"
            }
        }'
curl "https://api.basistheory.com/proxy" \
      -H "BT-API-KEY: key_NS21v84n7epsSc5WzoFjM6" \
      -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": "{{b78b4bee-5499-42dd-8671-f1d23d32355b | $.number}}",
                "month": "{{b78b4bee-5499-42dd-8671-f1d23d32355b | $.expiration_month}}",
                "year": "{{b78b4bee-5499-42dd-8671-f1d23d32355b | $.expiration_year}}",
                "verification_value": "{{b78b4bee-5499-42dd-8671-f1d23d32355b | $.cvc}}",
                "full_name": "{{523949a9-e32f-4b5b-a0ad-7a435c79deb4}} {{42af9170-e6ca-4ea7-a43b-730a0b47b6d0}}"
              },
              "retained": true
            }
          }'

Detokenized Request

{
  "args": {
    "card": {
      "number": "5105105105105100",
      "expiration_month": 5,
      "expiration_year": 2025,
      "cvc": "123"
    },
    "card_owner_full_name": "John Brown"
  }, 
  "configuration": {...}
}
{
  "payment_method": {
    "credit_card": {
      "number": "5105105105105100",
      "month": "5",
      "year": "2025",
      "verification_value": "123",
      "full_name": "John Brown"
    },
    "retained": true
  }
}

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.

Using Custom Token Schemas

Original Request

curl "https://api.basistheory.com/reactors/d08bc998-9301-495c-a2e5-04f8dc0916b4/react" \
      -H "BT-API-KEY: key_NS21v84n7epsSc5WzoFjM6" \
      -H "Content-Type: application/json" \
      -X "POST" \
      -d '{
            "args": {
              "card": {
                "number": "{{9a48a051-972b-4569-8fd5-cbe17a604f96 | $.card.card_number}}",
                "expiration_month": "{{9a48a051-972b-4569-8fd5-cbe17a604f96 | $.card.exp_month}}",
                "expiration_year": "{{9a48a051-972b-4569-8fd5-cbe17a604f96 | $.card.exp_year}}",
              },
              "card_owner_full_name": "{{9a48a051-972b-4569-8fd5-cbe17a604f96 | $.card.owner.first_name}} {{9a48a051-972b-4569-8fd5-cbe17a604f96 | $.card.owner.last_name}}"
            }
        }'
curl "https://api.basistheory.com/proxy" \
      -H "BT-API-KEY: key_NS21v84n7epsSc5WzoFjM6" \
      -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": "{{9a48a051-972b-4569-8fd5-cbe17a604f96 | $.card.card_number}}",
                "month": "{{9a48a051-972b-4569-8fd5-cbe17a604f96 | $.card.exp_month}}",
                "year": "{{9a48a051-972b-4569-8fd5-cbe17a604f96 | $.card.exp_year}}",
                "full_name": "{{9a48a051-972b-4569-8fd5-cbe17a604f96 | $.card.owner.first_name}} {{9a48a051-972b-4569-8fd5-cbe17a604f96 | $.card.owner.last_name}}"
              },
              "retained": true
            }
          }'

Detokenized Request

{
  "args": {
    "card": {
      "number": "4000056655665556",
      "expiration_month": 4,
      "expiration_year": 2026,
    },
    "card_owner_full_name": "John Smith"
  }, 
  "configuration": {...}
}
{
  "payment_method": {
    "credit_card": {
      "number": "4000056655665556",
      "month": "4",
      "year": "2026",
      "full_name": "John Smith"
    },
    "retained": true
  }
}

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"
    }
  },
  "privacy": {
    "classification": "pci",
    "impact_level": "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.