Card Issuing
Card Reveal
Column's card reveal SDKs show a card's full number, CVV, expiry and cardholder name to the cardholder, in your web page or your React Native app. The card data never passes through your code or your servers, so showing it doesn't bring them into PCI scope.
- Web:
@column-na/card-reveal, from npm or as a hosted script. - React Native and Expo:
@column-na/card-reveal-react-native.
The card reveal SDKs are proprietary and require an agreement with Column. Contact your Column representative to get started.
How It Works
- Your server creates a single-use reveal token for the card with
create card reveal token, using your API key, and sends only the token's
idto the browser or app. - The SDK reveals the card with that token. It generates a one-time key for each reveal and calls Column's API
directly. Column encrypts the card details end to end to that key, so nothing between Column and the SDK can read
them.
- On the web, each value renders inside its own iframe, served by Column from
https://reveal.column.com. Your page's JavaScript can't read it. - In React Native, each value renders inside the SDK's own text component. No callback, ref or return value exposes it.
- On the web, each value renders inside its own iframe, served by Column from
A reveal token is valid for one minute and works once, for one card. Create a new token for every reveal.
Server: Create a Reveal Token
Create the token right before you show the card, and only after you've checked that the signed-in user may see it.
curl 'https://api.column.com/issuing/cards/<card_id>/reveal-tokens' \
-XPOST \
-u :<YOUR API KEY>{
"id": "crtk_2Z1qT1aB2cD3eF4gH5iJ6kL7mN8",
"card_id": "card_2Z1qT0kM6yX9wB3nV5cR8sL4pA7",
"expires_at": "2026-10-23T18:01:00Z"
}Send the token id and the card ID to the browser or app. Don't call GET /issuing/cards/:card_id/reveal yourself:
only Column's SDKs can call it, because the response is encrypted to a key the SDK generates. Fetching card details
on your servers would also put them in PCI scope.
Web
Install
Install from npm, pinned to an exact version:
npm install --save-exact @column-na/card-reveal@0.6.0Or load the hosted script, pinned to an exact version with
Subresource Integrity↗. The hosted
script won't create a client unless its <script> tag has an integrity attribute. Each version is published once
and never changes.
<script
src="https://reveal.column.com/0.6.0/card-reveal.js"
integrity="sha384-Ep7c7/UaCPL1WbDDj1eKtsEttBjBCSwCttMpCPyk0A+XyMn9Gk9Vzl8A0xikWQuW"
crossorigin="anonymous"
></script>0.6.0https://reveal.column.com/0.6.0/card-reveal.jssha384-Ep7c7/UaCPL1WbDDj1eKtsEttBjBCSwCttMpCPyk0A+XyMn9Gk9Vzl8A0xikWQuWReveal a Card
import { createCardRevealClient, CardRevealError } from '@column-na/card-reveal';
// Hosted script: const { createCardRevealClient, CardRevealError } = window.ColumnCardReveal;
// Create one client and reuse it.
const client = createCardRevealClient({ environment: 'live' }); // or 'sandbox'
const { ready, unmount } = client.reveal({
cardId: 'card_2Z1qT0kM6yX9wB3nV5cR8sL4pA7',
revealToken: 'crtk_2Z1qT1aB2cD3eF4gH5iJ6kL7mN8',
fields: {
pan: '#card-number',
cvv: '#cvv',
cardholder_name: '#name',
expiration_month: '#exp-month',
expiration_year: '#exp-year',
},
style: { color: '#fff', 'font-family': 'ui-monospace, monospace', 'font-size': '15px' },
fieldStyles: { pan: { 'font-size': '19px' } },
format: { pan: 'spaced', expirationYear: 'YY' },
});
try {
await ready;
} catch (error) {
if (CardRevealError.is(error)) showMessage(error.code);
}
// When the cardholder hides the card, or the page or component goes away:
unmount();Each field renders as an iframe that fills its container, so give each container a width and height. In React, call
reveal in an effect and return unmount as its cleanup.
Content Security Policy
If your page has a Content Security Policy, allow Column's reveal frame:
script-src 'self' https://reveal.column.com;
frame-src https://reveal.column.com;Leave https://reveal.column.com out of script-src if you install from npm. Your page doesn't need any
connect-src entry or 'unsafe-inline' for the SDK, in sandbox or live.
React Native
The React Native SDK is pure JavaScript with no native module. It works in Expo Go, Expo development builds and bare React Native, and needs React 19.1 and React Native 0.81 or later.
Install
npx expo install @column-na/card-reveal-react-native@0.6.0 react-native-get-random-valuesCheck that package.json lists "@column-na/card-reveal-react-native": "0.6.0" with no ^ or ~, or install with
npm install --save-exact @column-na/card-reveal-react-native@0.6.0 react-native-get-random-values.
Each reveal generates a one-time key, which needs a secure random source. Import the polyfill once, at the top of your app's entry file, before anything else:
import 'react-native-get-random-values';Without it, reveal() throws invalid_options.
Reveal a Card
import { useEffect, useMemo, useRef } from 'react';
import { View } from 'react-native';
import {
CardRevealError,
CardRevealText,
createCardRevealClient,
type CardRevealTextHandle,
} from '@column-na/card-reveal-react-native';
export function CardDetails({ cardId, revealToken }: { cardId: string; revealToken: string }) {
const client = useMemo(() => createCardRevealClient({ environment: 'live' }), []); // or 'sandbox'
const pan = useRef<CardRevealTextHandle>(null);
const cvv = useRef<CardRevealTextHandle>(null);
useEffect(() => {
const { ready, unmount } = client.reveal({
cardId,
revealToken, // single use: create a new one for every reveal
fields: { pan, cvv },
style: { color: '#fff', fontFamily: 'Menlo' },
format: { pan: 'spaced' },
});
ready.catch((error) => {
if (CardRevealError.is(error)) console.warn('Card reveal failed:', error.code);
});
return unmount;
}, [client, cardId, revealToken]);
return (
<View>
<CardRevealText ref={pan} placeholder="•••• •••• •••• ••••" />
<CardRevealText ref={cvv} placeholder="•••" />
</View>
);
}CardRevealText shows its placeholder before the reveal and after unmount. Set selectable to let the cardholder
select and copy the value. React Native Web isn't supported for live reveals; use the web SDK instead.
Reference
Reveal Options
cardIdcard_...).revealTokencrtk_...) from your server.fieldspan, cvv, cardholder_name, expiration_month and expiration_year to where it renders: a CSS selector on the web, a CardRevealText ref in React Native. Include only the fields you want to show.style'&:hover', @media and @font-face blocks. In React Native, text styles.fieldStylesstyle.format.pan'spaced' (default, 4111 1111 1111 1111) or 'plain'.format.expirationYear'YYYY' (default) or 'YY'.timeoutMsready waits before it rejects with timeout. Default 15000, maximum 120000.ready and unmount
readyresolves once every requested field shows its value.- If any field fails or the timeout passes,
readyrejects and every field is removed. A reveal shows all of its fields or none of them. unmount()removes every field. Ifreadyis still pending, it rejects withunmounted.- Invalid options throw
invalid_optionssynchronously fromreveal().
Errors
Every error is a CardRevealError with a stable code. Check with CardRevealError.is(error). field and the HTTP
status are set when known. Messages never contain the token or card data, and the SDKs never log either.
invalid_optionsintegrity_required<script> tag has no integrity attribute.script_load_failedreveal() retries.container_not_foundreveal_failed401 status means the token is missing, invalid, already used, expired or for a different card.timeouttimeoutMs passed before every field showed its value.unmountedunmount() was called before the reveal finished.Sandbox
Sandbox works the same way, with a client created with environment: 'sandbox' and a reveal token for a sandbox
card, so you can build and test the whole flow before going live. See
Card Sandbox Simulation to create a sandbox card.
Versions
The web and React Native SDKs follow semantic versioning and release together at the same version. Pin an exact version. Each request tells Column which SDK and version made it, and Column may require a minimum version to keep revealing cards. Versions before 0.5.0 no longer reveal cards; upgrade to the latest version.