This page assumes you’ve read Secure Elements Overview — it covers loading the SDK and the shared client-token flow, both of which apply here.
See it live
The demo mints its own token and mounts the fields automatically. Enter any card number that passes a Luhn check, fill in the cardholder name, and submit. Use Remint token & remount if the form stops responding. Open it in its own tab →
Mint a tokenization token
CallPOST /v1/client-token with "purpose": "tokenization":
virtualCardId. It does need a different scope on your access token — MANAGE_PAYMENT, not CREATE_VIRTUALCARD — and it’s longer-lived (30 minutes by default) since a user filling out a card form takes longer than a reveal click.
Render the fields
renderFieldsForTokenization has no fields option — PAN, expiry, and CVV always mount together as one combined frame, because CVV validation is brand-aware (an Amex CVV is 4 digits; every other brand is 3), which only works if the field knows the card number typed into the frame next to it. You can’t mount them independently the way createCardViewer’s fields can be.
Mount it
FluzElementsError for INVALID_STYLE, MOUNT_TIMEOUT, or MOUNT_FAILED (frame failed to load, or this instance is already mounted). frameHostOrigin is validated synchronously when you call renderFieldsForTokenization, the same as createCardViewer — an unrecognized origin throws INVALID_FRAME_HOST_ORIGIN before you ever reach mount().
Track field state
brand is only present on pan’s state, detected from the digits typed so far: amex, visa, mastercard, discover, diners, or jcb. Use isValid to gate your own submit button and drive inline validation messaging — none of these fields expose the underlying value to your page.
excludedCardBrands (e.g. ["amex"]) doesn’t block typing — it forces pan’s isValid to false once a matching brand is detected, so a user can still enter the number but submit() won’t succeed until they use a different card.
Submit
Collect the cardholder name and billing address as ordinary inputs on your own page — the SDK doesn’t render them inside a Fluz-hosted frame, since they aren’t card data. Whether handling them yourself affects your own PCI DSS scope depends on your broader cardholder data environment; confirm with your QSA.cardholderName is split into first/last on the first space only — "Mary Ann Smith" becomes first name "Mary", last name "Ann Smith"; a single-word name is used as both. To reuse an address already on the account instead of collecting a new one, pass billingAddress: { userAddressId: "<uuid>" }.
submit() almost never rejects, and never for a decline. It only throws synchronously for MOUNT_FAILED (not mounted yet) or SUBMIT_FAILED (“a submit() call is already in progress” — it ignores a second call while one is in flight). Every other outcome — success, decline, validation failure, timeout — resolves normally and arrives through the callbacks below instead.Handle results
onSuccess fires once the card is added as a funding source. onDeclined fires for a card the processor rejected — still a normal, expected outcome, not an error:
onError is for everything that isn’t a normal decline:
Cleanup
Full example
/mint-tokenization-token is your own backend route — the one that calls POST /v1/client-token with "purpose": "tokenization" and your Fluz OAuth access token.
Next steps
Secure Elements Overview
Token minting, SDK loading, and CSP.
Card Reveal
The other Secure Elements capability — showing a user their own card details.
Live demo
Try the card-add form running against staging.
Example integrations
Runnable plain-HTML and React secure-card-input examples with a token-minting server.