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
},
});| Field | Type | Required | Description |
|---|---|---|---|
card | CardElement | Conditional | The combined card element. Required for combined input. |
cardNumber | CardNumberElement | Conditional | Card-number element. Required for split input. |
expiration | CardExpirationElement | Conditional | Expiry element. Required for split input. Note the key is expiration. |
cvc | CardCvcElement | Conditional | CVC element. Required for split input. |
cardholderName | CardholderNameElement | string | No | Name on the card, as a mounted element or a string. |
billingAddress | BillingAddressInput | No | Billing 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)
}| Field | Type | Description |
|---|---|---|
tokenId | string | The token reference to send to your backend. Prefixed tok_. |
type | 'card' | Always card for card tokenization. |
brand | string | Detected card brand (e.g. visa, mastercard). |
last4 | string | Last four digits, safe to display. |
expiryMonth | string | Two-digit expiry month as a string. |
expiryYear | string | Four-digit expiry year as a string. |
bin | string? | Bank identification number, when available. |
createdAt | string? | 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.
- Open card.tokeflow.com and enter your merchant's public key (
pk_test_…orpk_live_…). Secret keys are never used in the browser. - 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.
- Inspect the returned token reference (
tok_…) together with its non-sensitive details (brand,last4, expiry). - Pass that value as
card_ciphertext_idto test your payment-method endpoints — for examplePOST /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);
}
}
}| Code | When it happens |
|---|---|
ELEMENT_INCOMPLETE | A required card field is not fully entered. |
ELEMENT_INVALID | An element's value is invalid. |
INVALID_CARD | The card number failed validation. |
EXPIRED_CARD | The card's expiry date is in the past. |
UNSUPPORTED_CARD_BRAND | The card brand is not enabled for this merchant. |
TOKENIZATION_FAILED | Tokenization could not be completed. |
NOT_INITIALIZED | tokenize() was called before init() resolved. |
ELEMENT_NOT_MOUNTED | A passed element was not mounted to the DOM. |
NETWORK_ERROR | The 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
TokeflowErrorCodereference. - Authentication — public vs. secret keys and key scoping.