Identities

PII Tokenisation in Blnk

Replace sensitive identity fields with tokens, and read the original values back.

PII tokenization replaces a sensitive identity field with a token. Blnk keeps the token on the identity, and returns the original value only when you detokenize that field.

Detokenize reads the value and does not write the original back onto the identity. Updating the identity or its metadata does not clear the tokenization record. A client cannot set or erase tokenized_fields.

Turn it on with a tokenization_secret that is exactly 32 bytes. See Server security.

Tokenizable fields

Identity JSON uses snake_case. Tokenize and detokenize use the PascalCase name. They are not interchangeable. first_name returns IDT_FIELD_NOT_TOKENIZABLE. FirstName is the name Blnk accepts.

Identity JSON fieldTokenization name
first_nameFirstName
last_nameLastName
other_namesOtherNames
email_addressEmailAddress
phone_numberPhoneNumber
streetStreet
post_codePostCode

Use the PascalCase name in the URL and in the fields array. Blnk stores a format-preserving token in the field. The token starts with FPT: and keeps the shape of the original value, so an email still has an @ and a domain.

Tokenize one field

Call Tokenize field. The field must be in the table above, and it must not already be tokenized.

curl -X POST "http://localhost:5001/identities/{identity_id}/tokenize/EmailAddress" \
  -H "X-blnk-key: <api-key>"
const response = await blnk.Identity.tokenizeField(
  'idt_3b63c8da-af29-4cc3-ad38-df17d87456e6',
  'EmailAddress',
);
tokenized, _, _ := client.Identity.TokenizeField(
    "idt_3b63c8da-af29-4cc3-ad38-df17d87456e6",
    string(blnkgo.TokenizableFieldEmailAddress),
)
response = blnk.identity.tokenize_field(
  "idt_3b63c8da-af29-4cc3-ad38-df17d87456e6",
  "EmailAddress",
)
ApiResponse<JsonNode> response = blnk.identity().tokenizeField(
    "idt_3b63c8da-af29-4cc3-ad38-df17d87456e6",
    "EmailAddress");
FieldDescriptionRequired
identity_idIdentity that holds the field.Yes
fieldPascalCase field name, such as EmailAddress.Yes
{
  "message": "Field tokenized successfully"
}
FieldDescription
messageConfirms the field is now a token.

Tokenize several fields

Using the API

Call Tokenize identity with at least one field. Blnk tokenizes them in order and stops on the first field that fails.

curl -X POST "http://localhost:5001/identities/{identity_id}/tokenize" \
  -H "X-blnk-key: <api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": ["FirstName", "LastName", "EmailAddress", "PhoneNumber"]
  }'
const response = await blnk.Identity.tokenize(
  'idt_3b63c8da-af29-4cc3-ad38-df17d87456e6',
  {
    fields: ['FirstName', 'LastName', 'EmailAddress', 'PhoneNumber'],
  },
);
tokenized, _, _ := client.Identity.Tokenize(
    "idt_3b63c8da-af29-4cc3-ad38-df17d87456e6",
    blnkgo.TokenizeRequest{
        Fields: []blnkgo.TokenizableIdentityField{
            blnkgo.TokenizableFieldFirstName,
            blnkgo.TokenizableFieldLastName,
            blnkgo.TokenizableFieldEmailAddress,
            blnkgo.TokenizableFieldPhoneNumber,
        },
    },
)
response = blnk.identity.tokenize(
  "idt_3b63c8da-af29-4cc3-ad38-df17d87456e6",
  {
    "fields": ["FirstName", "LastName", "EmailAddress", "PhoneNumber"],
  },
)
ApiResponse<JsonNode> response = blnk.identity().tokenize(
    "idt_3b63c8da-af29-4cc3-ad38-df17d87456e6",
    TokenizeIdentityData.create()
        .fields(List.of("FirstName", "LastName", "EmailAddress", "PhoneNumber")));
FieldDescriptionRequired
identity_idIdentity that holds the fields.Yes
fieldsPascalCase names to tokenize. At least one.Yes
{
  "message": "Fields tokenized successfully"
}
FieldDescription
messageConfirms every field in the request is now a token.

Using the Dashboard

Open the identity, then click Actions in the top right.

Tokenize fields

Select Tokenize. In the modal, choose the fields, then click Submit.

Actions menu with Tokenize

Tokenize modal with field selection

See which fields are tokenized

Call Get tokenized fields.

curl -X GET "http://localhost:5001/identities/{identity_id}/tokenized-fields" \
  -H "X-blnk-key: <api-key>"
const response = await blnk.Identity.getTokenizedFields(
  'idt_3b63c8da-af29-4cc3-ad38-df17d87456e6',
);
fields, _, _ := client.Identity.GetTokenizedFields(
    "idt_3b63c8da-af29-4cc3-ad38-df17d87456e6",
)
response = blnk.identity.get_tokenized_fields(
  "idt_3b63c8da-af29-4cc3-ad38-df17d87456e6",
)
ApiResponse<JsonNode> response = blnk.identity().getTokenizedFields(
    "idt_3b63c8da-af29-4cc3-ad38-df17d87456e6");
{
  "tokenized_fields": ["FirstName", "LastName", "EmailAddress", "PhoneNumber"]
}
FieldDescription
tokenized_fieldsPascalCase names that are tokenized on this identity.

Read one field back

Call Detokenize field. The field must already be tokenized.

curl -X GET "http://localhost:5001/identities/{identity_id}/detokenize/EmailAddress" \
  -H "X-blnk-key: <api-key>"
const response = await blnk.Identity.detokenizeField(
  'idt_3b63c8da-af29-4cc3-ad38-df17d87456e6',
  'EmailAddress',
);
detokenized, _, _ := client.Identity.DetokenizeField(
    "idt_3b63c8da-af29-4cc3-ad38-df17d87456e6",
    string(blnkgo.TokenizableFieldEmailAddress),
)
response = blnk.identity.detokenize_field(
  "idt_3b63c8da-af29-4cc3-ad38-df17d87456e6",
  "EmailAddress",
)
ApiResponse<JsonNode> response = blnk.identity().detokenizeField(
    "idt_3b63c8da-af29-4cc3-ad38-df17d87456e6",
    "EmailAddress");
FieldDescriptionRequired
identity_idIdentity that holds the token.Yes
fieldPascalCase field name to read.Yes
{
  "field": "EmailAddress",
  "value": "[email protected]"
}
FieldDescription
fieldPascalCase name you asked for.
valueOriginal value. The identity still stores the token.

Read several fields back

Using the API

Call Detokenize identity. The keys in the response are the names you sent. Omit fields, or send an empty array, to read every tokenized field.

curl -X POST "http://localhost:5001/identities/{identity_id}/detokenize" \
  -H "X-blnk-key: <api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": ["FirstName", "LastName", "EmailAddress", "PhoneNumber"]
  }'
const response = await blnk.Identity.detokenize(
  'idt_3b63c8da-af29-4cc3-ad38-df17d87456e6',
  {
    fields: ['FirstName', 'LastName', 'EmailAddress', 'PhoneNumber'],
  },
);
detokenized, _, _ := client.Identity.Detokenize(
    "idt_3b63c8da-af29-4cc3-ad38-df17d87456e6",
    blnkgo.DetokenizeRequest{
        Fields: []blnkgo.TokenizableIdentityField{
            blnkgo.TokenizableFieldFirstName,
            blnkgo.TokenizableFieldLastName,
            blnkgo.TokenizableFieldEmailAddress,
            blnkgo.TokenizableFieldPhoneNumber,
        },
    },
)
response = blnk.identity.detokenize(
  "idt_3b63c8da-af29-4cc3-ad38-df17d87456e6",
  {
    "fields": ["FirstName", "LastName", "EmailAddress", "PhoneNumber"],
  },
)
ApiResponse<JsonNode> response = blnk.identity().detokenize(
    "idt_3b63c8da-af29-4cc3-ad38-df17d87456e6",
    DetokenizeIdentityData.create()
        .fields(List.of("FirstName", "LastName", "EmailAddress", "PhoneNumber")));
FieldDescriptionRequired
identity_idIdentity that holds the tokens.Yes
fieldsPascalCase names to read. Omit to read every tokenized field.No
{
  "fields": {
    "FirstName": "Alice",
    "LastName": "Smith",
    "EmailAddress": "[email protected]",
    "PhoneNumber": "+1234567890"
  }
}
FieldDescription
fieldsMap of the names you sent to their original values.

Using the Dashboard

Open the identity, then click Actions in the top right.

Read the original values

Select Detokenize. The identity details show the original values for the tokenized fields. The stored values stay tokens.

Actions menu with Detokenize

Errors

CodeWhen it happens
IDT_FIELD_NOT_TOKENIZABLEThe name is not in the tokenizable set, or the casing is wrong.
IDT_FIELD_ALREADY_TOKENIZEDYou tokenize a field that is already a token.
IDT_FIELD_NOT_TOKENIZEDYou detokenize a field that is not a token.
IDT_TOKENIZATION_DISABLEDtokenization_secret is missing or is not 32 bytes.
{
  "error": "field first_name is not tokenizable",
  "error_detail": {
    "code": "IDT_FIELD_NOT_TOKENIZABLE",
    "message": "field first_name is not tokenizable"
  }
}
CodeWhat to do
IDT_FIELD_NOT_TOKENIZABLEUse the PascalCase name, such as /tokenize/FirstName.
IDT_FIELD_ALREADY_TOKENIZEDSkip it, or detokenize first if you need the original value.
IDT_FIELD_NOT_TOKENIZEDTokenize the field before you detokenize it.
IDT_TOKENIZATION_DISABLEDSet tokenization_secret to a 32-byte value and restart Core.

For the full list, see API error codes.