What Secure Elements is
Secure Elements is a JavaScript SDK,@fluz/secure-elements, that mounts an isolated, Fluz-hosted frame directly into a container element on your page. The frame renders or collects sensitive data; your page and your servers only ever hold a short-lived, opaque token that authorizes one specific action. It covers three capabilities:
- Card Reveal — show a user their own PAN, expiry, and CVV.
- Secure Card Input — collect a physical card and add it as a payment method on their account.
- VAN Reveal — show a user the account and routing number of their own virtual account number.
The same tradeoff applies to collecting a card: a widget lets Fluz capture the PAN inside its own PCI scope, and Secure Card Input gets you the same “never touches your servers” property inline in your own page instead of a modal.
VAN Reveal follows the same pattern as Card Reveal, but for a virtual account number’s account and routing number instead of a card’s PAN, expiry, and CVV.
If you’re already using the Embedded Widget for
everything, you don’t need this. Secure Elements is for headless or API-driven
integrations that still need to show or collect card details without opening
the full widget modal or pursuing
PCI_COMPLIANCE.How it works
Your backend mints a client token
Exchange your existing Fluz OAuth access
token for a short-lived
client token, scoped to a single reveal or a single card-add.
Your frontend mounts the frame
Hand the client token to
@fluz/secure-elements, which mounts the
Fluz-hosted frame into a container you provide — inline in your page, not a
modal.The SDK reports back via callbacks
Your page never reads the raw card data. It only sees success, error, or
mount events.
Prerequisites
-
Your application is registered with Fluz. The scope required on your access token depends on which capability you’re minting a client token for:
-
For a reveal token specifically, you also need an
ACTIVEvirtual card id, owned by the account you’re revealing, to pass when minting it. -
For a van-reveal token specifically, you also need a
virtualAccountNumberId, owned by the account you’re revealing, to pass when minting it — the same idgetSpendAccountVirtualAccountNumbersreturns, and the sameLIST_PAYMENTscope that call already requires.
Environments
frameHostOrigin is optional on createCardViewer — omit it and it defaults to production (https://secure.fluz.app). Pass it explicitly to target staging. Only these two exact origins are accepted; anything else throws a FluzElementsError (error.code === "INVALID_FRAME_HOST_ORIGIN") as soon as you call createCardViewer, before any frame is mounted.
Loading the SDK
@fluz/secure-elements isn’t published to npm — load it as a browser global (IIFE) build from Fluz’s CDN with a <script> tag. It exposes a FluzSecureElements global:
FluzSecureElements, as above.
Each release publishes to an immutable, version-pinned path (.../v0.1.0/index.global.js) and a floating .../latest/index.global.js that always points at the newest release. Pin to a specific version for anything beyond a prototype — latest can change under you without notice.
Mint a client token
Your backend calls this using the Fluz OAuth access token you already obtain through the standard OAuth grant flow. Never send that access token to the browser — only theclientToken / loadToken pair this endpoint returns should reach your frontend.
purpose is "reveal" (with a virtualCardId), "tokenization" (no virtualCardId — see Secure Card Input), or "van-reveal" (with a virtualAccountNumberId instead of a virtualCardId — see VAN Reveal).
201. Both tokens are single-purpose and short-lived — mint a new pair every time. clientToken is what authorizes the reveal or card-add itself (expiresIn seconds — 300 by default for reveal, 1800 for tokenization); loadToken is scoped even tighter (60 seconds) since it travels in a URL and is rejected everywhere except loading the frame. Pass both straight into createCardViewer or renderFieldsForTokenization, and never put clientToken in a URL yourself — the SDK already keeps it out of one.
Error responses
Error responses
| Status | Error | Meaning | | :— | :— | :— | | 400 |
invalid_purpose |
purpose missing or invalid | | 400 | virtual_card_id_required | missing
virtualCardId for a reveal token | | 400 | virtual_account_number_id_required |
missing virtualAccountNumberId for a van-reveal token | | 401 | unauthorized | missing access
token | | 401 | invalid_token | access token failed verification (malformed,
expired, wrong signature) | | 403 | insufficient_scope | access token
missing the scope required for this purpose — see
Prerequisites | | 403 | app_not_registered | your
application isn’t registered — contact Fluz | | 403 | forbidden | card or
virtual account number not found, or not owned by this user | | 429 | rate_limited | too many
client-token requests for this user — see the Retry-After header | | 500 |
internal_error | unexpected server error |Styling fields
createCardViewer, createAccountViewer, and renderFieldsForTokenization all accept an optional style object, applied to every field they mount:
style is validated before anything is sent to the frame. If a value doesn’t match what’s documented below, await viewer.mount(...) rejects with a FluzElementsError (error.code === "INVALID_STYLE") — wrap your mount() call in a try/catch if you’re accepting configurable style input yourself.
width, height, border, and backgroundColor style the box around each field — but which element that is differs by capability, since reveal and Secure Card Input mount different DOM shapes:
createCardViewer/createAccountViewer(reveal) — each field is its own frame holding a single value, rendered as oneinline-blockelement, so these four properties apply directly to it.renderFieldsForTokenization(Secure Card Input) — the fields share one frame, each inside its own wrapper box. These properties apply to that wrapper, not the input text itself. See Box-model styling and Layout on the Secure Card Input page for thelayoutoption this also unlocks.
fontFamily must be an exact, case-sensitive match for one of two allowlists:
- System fonts — common OS/web-safe stacks (
system-ui,-apple-system,Helvetica Neue,Arial,Georgia,Menlo, and the genericmonospace/serif/sans-serifkeywords, among others). These render immediately, with no network request. - Google Fonts — any family from the Google Fonts catalog (
"Roboto","Inter","IBM Plex Mono", and so on), passed exactly as Google lists it. The SDK loads the font for you — you don’t need a<link>tag or@font-facerule.
A Google Font is fetched after the field mounts, not bundled up front, so
there’s a brief window on a cold cache where the field renders in the
browser’s fallback font before swapping in your chosen one. A system font has
no such delay.
Content Security Policy
If your page sets a CSP, allow the frame host you’re targeting, and the CDN host the SDK loads from:script-src’s allowed host; the inline <script> block in your own integration code (as in the examples on this page and on Card Reveal, Secure Card Input, and VAN Reveal) still needs its own nonce or hash if your CSP doesn’t allow 'unsafe-inline'. If you use a nonce, the value on the tag must match the one in the header exactly:
Security model
- Your OAuth access token never leaves your servers.
- The client token your frontend holds is opaque and single-purpose — it carries no card data and can’t be replayed for a different card or action.
- Card data is only ever readable inside Fluz-hosted frames, isolated from your page’s own JavaScript. Every frame is sandboxed (
allow-scripts allow-same-origin allow-forms) withreferrerPolicy="no-referrer"— the SDK never puts card data in the DOM outside of them. - The frame only renders inside origins you’ve pre-registered with Fluz.
- Card data never reaches your servers or your page’s JavaScript — this is the condition most relevant to SAQ A eligibility under PCI DSS, alongside the other requirements SAQ A sets (e.g. not otherwise storing, processing, or transmitting cardholder data yourself). Your applicable PCI DSS obligations depend on your full cardholder data environment; confirm your scope with your QSA.
Next steps
Card Reveal
Create the card viewer, mount it, and control which fields are revealed.
Secure Card Input
Collect a physical card and add it as a payment method.
VAN Reveal
Show a user the account and routing number of their own virtual account number.
Live demos
See all three capabilities running against staging.
Example integrations
Runnable plain-HTML and React examples, both calling real staging
infrastructure.
OAuth applications
How to obtain the access token you’ll exchange for a client token.