Tokenization

Encrypt card data in the browser with Tokeflow's Bridge SDK and exchange it for a single-use token reference your backend can charge.

Tokenization turns raw card data into a safe-to-handle token reference without that data ever touching your servers. With the Bridge SDK, the cardholder types into Tokeflow's secure fields, the SDK encrypts the data in the browser, and tokeflow.cards.tokenize() returns a CardToken. You send only the tokenId to your backend, which creates a transaction with it.

This keeps sensitive card data out of your application's scope while letting you keep full control of the UI and the payment flow.

Tokeflow's secure fields run inside isolated, cross-origin iframes; card data is encrypted in the browser and never touches your servers. Page scripts cannot read the raw PAN, expiry, or CVC.

How tokenization fits in

Tokenization is a client-side step. The token it produces is exchanged for a charge on your server, where your secret key lives.

Never send raw card data (PAN, CVC, expiry) to your own backend. Send only the tokenId returned by tokenize(). The whole point of tokenization is to keep card data out of your servers' PCI scope.

tokeflow.cards.tokenize(input, options?)

The cards.tokenize() method encrypts the data collected by your card Elements and resolves to a CardToken. It accepts two input shapes — combined or split — plus optional cardholder name and billing address.

Call tokenize() only after the SDK is initialized (await tokeflow.init()) and your card element(s) are mounted. See Quickstart for the full setup.

Combined-element input

Use the combined CardElement (number, expiry, and CVC in a single iframe) and pass it as card:

const cardElement = tokeflow.createCardElement();
cardElement.mount('#card-element');

const token = await tokeflow.cards.tokenize({ card: cardElement });
console.log(token.tokenId); // tok_...

Split-fields input

When you collect the card number, expiry, and CVC in separate iframes, pass each element under its own key:

const cardNumber = tokeflow.createCardNumberElement();
const cardExpiry = tokeflow.createCardExpirationElement();
const cardCvc = tokeflow.createCardCvcElement();

cardNumber.mount('#card-number');
cardExpiry.mount('#card-expiry');
cardCvc.mount('#card-cvc');

const token = await tokeflow.cards.tokenize({
  cardNumber,
  expiration: cardExpiry,
  cvc: cardCvc,
});

The expiration key is expiration — not expirationDate or expiry. Using the wrong key leaves the expiry unset and tokenization fails validation.

Optional cardholder name and billing address

Both inputs accept an optional cardholderName and billingAddress. Each field can be supplied as a mounted element or as a plain string — mix and match as your form requires.

const token = await tokeflow.cards.tokenize({
  card: cardElement,
  cardholderName: cardholderNameElement, // element or 'Jane Doe'
  billingAddress: {
    line1: addressLine1Element,          // element or string
    line2: 'Apt 4B',                     // string
    city: cityElement,                   // element or string
    state: 'CA',                         // string
    postalCode: postalCodeElement,       // element or string
    country: 'US',                       // ISO 3166-1 alpha-2
  },
});
FieldTypeRequiredDescription
cardCardElementConditionalThe combined card element. Required for combined input.
cardNumberCardNumberElementConditionalCard-number element. Required for split input.
expirationCardExpirationElementConditionalExpiry element. Required for split input. Note the key is expiration.
cvcCardCvcElementConditionalCVC element. Required for split input.
cardholderNameCardholderNameElement | stringNoName on the card, as a mounted element or a string.
billingAddressBillingAddressInputNoBilling address. Each subfield is an element or a string.

BillingAddressInput subfields: line1, line2, city, state, postalCode, country — every one accepts an element or a string.

Options

The optional second argument carries non-sensitive metadata to store alongside the token.

interface TokenizeOptions {
  metadata?: Record<string, any>; // custom metadata stored with the token
}
const token = await tokeflow.cards.tokenize(
  { card: cardElement },
  { metadata: { cartId: 'cart_123', source: 'web' } }
);

Never put cardholder PII or card data in metadata. It is non-sensitive custom data only.

There is no saveCard flag in the SDK and no token-intent vs. token distinction — tokenize() always returns a CardToken. Whether a card is persisted as a reusable instrument is governed by your backend / checkout configuration, not the SDK call. See Checkout.

The CardToken response

interface CardToken {
  tokenId: string;       // your Tokeflow token reference, e.g. 'tok_...'
  type: 'card';
  brand: string;         // 'visa', 'mastercard', etc.
  last4: string;         // last 4 digits of the card
  expiryMonth: string;   // e.g. '12'
  expiryYear: string;    // e.g. '2029'
  bin?: string;          // bank identification number (optional)
  createdAt?: string;    // ISO 8601 timestamp (optional)
}
FieldTypeDescription
tokenIdstringThe token reference to send to your backend. Prefixed tok_.
type'card'Always card for card tokenization.
brandstringDetected card brand (e.g. visa, mastercard).
last4stringLast four digits, safe to display.
expiryMonthstringTwo-digit expiry month as a string.
expiryYearstringFour-digit expiry year as a string.
binstring?Bank identification number, when available.
createdAtstring?ISO 8601 creation timestamp, when present.

Example value:

{
  "tokenId": "tok_8f3c2a1b9d4e",
  "type": "card",
  "brand": "visa",
  "last4": "4242",
  "expiryMonth": "12",
  "expiryYear": "2029",
  "bin": "424242",
  "createdAt": "2026-01-15T12:30:00.000Z"
}

brand, last4, bin, and the expiry are non-sensitive and safe to display in your UI — useful for an "ending in 4242" confirmation. The full PAN and CVC are never returned.

Test the Bridge SDK without code

Want a token to try the API before wiring up the SDK? Tokeflow hosts a tester at card.tokeflow.com that runs the Bridge SDK against your merchant and shows you the returned token — the same encrypted tok_… reference your own integration receives. Use it to see exactly what tokenization returns and to feed real token values into the payment endpoints.

  1. Open card.tokeflow.com and enter your merchant's public key (pk_test_… or pk_live_…). Secret keys are never used in the browser.
  2. Complete the secure card fields and submit. The card is encrypted inside Tokeflow's isolated, cross-origin iframes — exactly as on your own site — so the raw PAN never leaves the browser.
  3. Inspect the returned token reference (tok_…) together with its non-sensitive details (brand, last4, expiry).
  4. Pass that value as card_ciphertext_id to test your payment-method endpoints — for example POST /api/v1/transactions.

Each token is single-use and short-lived — generate a fresh one for every test charge. Use a test public key (pk_test_…) while integrating so no real funds are involved. See Environments and keys.

Exchanging the token for a charge

The token is a reference, not a charge. After tokenize() resolves, send the tokenId to your own backend and let your server create the transaction with its secret key.

1. Tokenize in the browser

const token = await tokeflow.cards.tokenize({ card: cardElement });

await fetch('/api/payments', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ tokenId: token.tokenId }),
});

2. Create the transaction from your server

Your backend exchanges the tokenId for a transaction. Use a secret key — never expose it in the browser — and send an Idempotency-Key so retries don't double-charge.

curl -X POST https://api.tokeflow.com/api/v1/transactions \
  -H "Authorization: Bearer sk_live_mer_xxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6b1f7e2c-9a44-4f1e-8c3d-1a2b3c4d5e6f" \
  -d '{
    "amount": 9900,
    "currency": "BRL",
    "payment_method": "credit_card",
    "card_ciphertext_id": "tok_8f3c2a1b9d4e",
    "customer_id": "cust_9f2a1c"
  }'

Example success response:

{
  "success": true,
  "data": {
    "id": "tx_4d2e1a9c7b",
    "status": "authorized",
    "amount": 9900,
    "currency": "BRL",
    "payment_method": "credit_card",
    "charge_type": "payment",
    "customer_id": "cust_9f2a1c",
    "created_at": "2026-01-15T12:30:01.000Z"
  },
  "request_id": "req_8f3c2a1b",
  "timestamp": "2026-01-15T12:30:01.000Z"
}

The amount is in integer minor units (cents). 9900 with "currency": "BRL" is R$99.00. Always send the ISO 4217 currency.

Money amounts and routing decisions are made on your server with your secret key. The browser never sees the secret key, the amount it cannot tamper with, or the connector that handles the charge. See Authentication for key scoping.

Error handling

tokenize() rejects with a TokeflowError carrying a machine-readable code. Handle the common cases to give the cardholder actionable feedback. See the full list on Error Handling.

import { TokeflowError, TokeflowErrorCode } from '@tokeflow_com/bridge-js';

try {
  const token = await tokeflow.cards.tokenize({ card: cardElement });
  // ... send token.tokenId to your backend
} catch (error) {
  if (error instanceof TokeflowError) {
    switch (error.code) {
      case TokeflowErrorCode.ELEMENT_INCOMPLETE:
        showError('Please complete all card fields');
        break;
      case TokeflowErrorCode.INVALID_CARD:
        showError('Please check your card number');
        break;
      case TokeflowErrorCode.EXPIRED_CARD:
        showError('This card has expired');
        break;
      case TokeflowErrorCode.UNSUPPORTED_CARD_BRAND:
        showError('This card brand is not supported');
        break;
      case TokeflowErrorCode.NETWORK_ERROR:
        showError('Network error. Please try again.');
        break;
      default:
        showError(error.message);
    }
  }
}
CodeWhen it happens
ELEMENT_INCOMPLETEA required card field is not fully entered.
ELEMENT_INVALIDAn element's value is invalid.
INVALID_CARDThe card number failed validation.
EXPIRED_CARDThe card's expiry date is in the past.
UNSUPPORTED_CARD_BRANDThe card brand is not enabled for this merchant.
TOKENIZATION_FAILEDTokenization could not be completed.
NOT_INITIALIZEDtokenize() was called before init() resolved.
ELEMENT_NOT_MOUNTEDA passed element was not mounted to the DOM.
NETWORK_ERRORThe request to Tokeflow failed at the network layer.

Read the element's getState() ({ empty, valid, invalid, complete }) before calling tokenize(), and disable your pay button until state.complete && state.valid. This catches incomplete input before a round trip. See Elements.

Next steps

  • Elements — create and style the secure fields you pass to tokenize().
  • Checkout — link a backend-created session to drive a full checkout flow.
  • Error Handling — the complete TokeflowErrorCode reference.
  • Authentication — public vs. secret keys and key scoping.

On this page