Skip to main content
Cards is available in sandbox only while we finish development. Dakota enables Cards per account. The Cards endpoints are in the API reference, marked Sandbox only. Endpoints, fields, and flows can still change before release.
This guide covers the one piece of backend you have to write — the reveal-session endpoint — plus how sessions behave, how to handle errors, and how to show more than one card on a page. This page is the reference for the SDK side. The Dakota REST call that your backend makes — its request, response, headers, and rules — is documented once, on Secure card data.

How a reveal works

  1. Your page creates a DakotaCards client and mounts a card. Mounting only paints the masked face. It makes no network calls.
  2. When the cardholder asks to see the details, the SDK calls your fetchSession function with a request context.
  3. fetchSession forwards that context to your backend. Your backend calls Dakota with your secret API key and returns a short-lived session token to the browser.
  4. The SDK hands the token to the card-data processor, which renders the real values inside frames your code cannot read.
The API key lives only on your server. The browser only ever holds a single-use session token that is worthless once consumed or expired.

The fetchSession contract

fetchSession is the only integration point you implement on the client. Its signature:
The SDK builds the 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.
Authenticate and authorize before you mint. A reveal session unlocks a specific card’s details in the browser, so minting one is a privileged action. Before your endpoint mints, it must authenticate the cardholder’s session and confirm that the requested card belongs to that cardholder. Skip either check, and anyone who can reach the endpoint can reveal the card it is bound to.
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:
A minimal Node handler, in Express style:
Illustrative draft — subject to change
Return an error status (4xx or 5xx) if the upstream call fails. The SDK surfaces a thrown 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 the copied 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 calling fetchSession again. 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 is min(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():
The card-details handle emits 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 a DakotaCardsError. 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

A DakotaCards 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.
In React, pass the per-card 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.