How a reveal works
- Your page creates a
DakotaCardsclient and mounts a card. Mounting only paints the masked face. It makes no network calls. - When the cardholder asks to see the details, the SDK calls your
fetchSessionfunction with a request context. fetchSessionforwards that context to your backend. Your backend calls Dakota with your secret API key and returns a short-lived session token to the browser.- The SDK hands the token to the card-data processor, which renders the real values inside frames your code cannot read.
The fetchSession contract
fetchSession is the only integration point you implement on the client. Its signature:
ctx for you. Your job is to POST it to your backend and return the { session, expiresAt } your backend gives back. Forward sessionType and origin unchanged — the sessionType names the flow requesting the session, and the origin is what the session is bound to.
provider is a reserved routing discriminator for future card-data providers. Leave it unset. When it is absent, the SDK uses its default provider.The backend endpoint
Your backend is the trust boundary: it holds the API key and mints sessions on the page’s behalf. It forwards the SDK’s{ sessionType, origin } to Dakota’s reveal-session endpoint for the card you are displaying.
Mint against the API environment that matches the environment you give DakotaCards: https://api.platform.dakota.xyz for production, https://api.platform.sandbox.dakota.xyz for sandbox. The request itself — headers, body, and response — is on Secure card data.
The Dakota API speaks snake_case and the SDK speaks camelCase, so your backend renames the two fields that differ — one on the way out, one on the way back. On the request, rename sessionType to session_type. On the response, rename expires_at to the expiresAt that the SDK expects:
Illustrative draft — subject to change
fetchSession as a retryable session_fetch_failed.
Copy to clipboard
The values never reach your JavaScript, so your code cannot copy them. The value frames are click-to-copy instead: the cardholder clicks a value, the frame performs the copy, and the SDK reports it through thecopied event ({ field, success }).
To offer a copy button, render your icon underneath a frame’s transparent tail — size the frame a little wider than the text — so that a click on the icon lands on the frame. Then flip the icon to a confirmation on the copied event.
Session semantics
- Single-use. Each session token is consumed by exactly one reveal. There is no caching or reuse: every
reveal()mints a fresh session by callingfetchSessionagain. The only sharing is deduplication of concurrent calls that are already in flight. - Short-lived. The token is valid until
expiresAt, capped at 10 minutes by the SDK regardless of what you send.
The reveal window & auto-mask
A successful reveal is always time-boxed. When the window ends, the SDK tears down the value frames itself and shows the masked face again — you do not have to. The window length ismin(autoMaskMs, remaining session TTL), and the session TTL is itself capped at 10 minutes. So even if you set no autoMaskMs, a reveal auto-masks when the session expires. When the window ends, the handle fires an expired event — versus a masked event for a reveal you dismissed yourself with mask():
ready, revealing, revealed, masked, expired, and copied. Errors are not delivered as events. They surface as rejections of the reveal() and mount() promises, so wrap those calls in try/catch. The one synchronous throw is invalid_theme, raised by cardDetails() itself when the handle is created.
A reserved error key does appear in the CardDetailsEvents type map, but the SDK never emits it in this version — see the API reference.
Error handling
Every error the SDK throws is aDakotaCardsError. Branch on error.retryable to decide whether a retry makes sense, and read error.code for the specific case. When the failure originated with the card-data processor, error.diagnosticId carries an upstream request ID that you can quote to Dakota support.
Error codes
Troubleshooting origin problems.
origin_mismatch is reserved and not thrown today. Origin problems currently surface as reveal_failed or session_rejected. If reveals consistently fail with either code, first verify that the origin your backend forwards to the reveal-session endpoint exactly matches the page’s origin — scheme, host, and port, with no path. A mismatch there is a common cause of both codes.Showing more than one card on a page
ADakotaCards client can drive any number of cards. A reveal session is minted for a specific card (/cards/{card_id}/reveal_session), so give each handle its own fetchSession that targets the right card. Each handle keeps its own session broker, so their reveals never interfere.
fetchSession as a prop to each <CardDetails> — or to useCardDetails. It overrides the client-level one for that handle.
Driving a loading treatment
reveal() emits revealing the moment an attempt starts. At the same moment, the face swaps to the frame side, where empty frames show the stylesheet’s loading state. The hook’s state reports the same value, so a custom UI can derive its in-flight treatment directly: disable the trigger, show a spinner, or animate a transition. A failed attempt emits masked on the way back, so event-driven UIs always see the attempt end.
Known sharp edge: mask() during an in-flight reveal
mask() only acts on an already-revealed card. If you call it while a reveal() is still in flight — the handle is in the revealing state — it is a no-op. It does not cancel the pending reveal. If you need a “cancel” affordance, disable it until the revealed event fires, or destroy() the handle to tear everything down.
