> ## Documentation Index
> Fetch the complete documentation index at: https://docs.yativo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Secure Card Display

> Display sensitive card details and PIN in a secure hosted iframe — card data and PIN each require a separate view token

<Warning>
  **Cardholders must set their PIN before card details or PIN reveal can be viewed.**

  If `pin_set` is `false` and you request `["data"]` or `["pin-view"]`, the API returns a `422 PIN_NOT_SET` error with a ready-to-use `pin_setup_url`. Open that URL so the cardholder can set their PIN, then retry the view-token request.

  To **set** the PIN, call with `enabled_views: ["pin-set"]` — this always returns **200 OK** and `secure_view_url`, regardless of `pin_set` status. See [Setting a Card PIN](/yativo-crypto/cards/set-pin).
</Warning>

<Note>
  Card details (PAN, CVV, expiry) and the card PIN each use **separate view tokens**. You must request the correct `enabled_views` for each use case. Never reuse a token between sessions.
</Note>

## How it works

Sensitive card data is never returned in plain API responses. Instead, Yativo hosts a secure card view page and provides you with a short-lived URL to embed in your application:

1. Your backend calls `POST /yativo-card/{yativoCardId}/cards/{cardId}/view-token`
2. If you request `["data"]` or `["pin-view"]` and `pin_set` is `false`, the API returns `PIN_NOT_SET` and a `pin_setup_url` — direct the cardholder there to set their PIN first. If you request `["pin-set"]`, you always receive a `secure_view_url` regardless of `pin_set` status.
3. Yativo returns a `secure_view_url` — a hosted, tokenized page
4. Your frontend embeds that URL in an `<iframe>`
5. The hosted page renders the sensitive data directly — your application never touches the raw values

<Warning>
  View tokens expire quickly. Always request a fresh token immediately before rendering and never cache or log `secure_view_url`.
</Warning>

***

## Step 1 — Request a view token

Call this from your **backend**. The token is scoped to the views you request.

```
POST /yativo-card/{yativoCardId}/cards/{cardId}/view-token
```

<ParamField path="yativoCardId" type="string" required>
  The Yativo Card account ID (`yativo_card_id`) returned during onboarding.
</ParamField>

<ParamField path="cardId" type="string" required>
  The card ID from card creation.
</ParamField>

<ParamField body="enabled_views" type="array">
  Which views to enable on the hosted page. Accepted values:

  | Value        | Tab shown                | Requires `pin_set: true`?                                         |
  | ------------ | ------------------------ | ----------------------------------------------------------------- |
  | `"data"`     | Card number, CVV, expiry | Yes — returns `422 PIN_NOT_SET` if PIN not set                    |
  | `"pin-view"` | Reveal current PIN       | Yes — returns `422 PIN_NOT_SET` if PIN not set                    |
  | `"pin-set"`  | Set or change PIN        | **No** — always returns `200` so the cardholder can set their PIN |

  The legacy value `"pin"` is accepted as an alias for `"pin-set"`.

  Combine freely, e.g. `["data", "pin-view", "pin-set"]`. The page renders one tab per value. If `pin_set` is `false` and `"pin-set"` is **not** in the request, the API returns `422 PIN_NOT_SET`. Including `"pin-set"` in any request bypasses that gate — the token is issued so the cardholder can set their PIN.
</ParamField>

<ParamField body="access_code" type="string">
  Optional extra PIN or unlock code the user must enter before the card details are revealed. Adds an additional layer of protection for high-security flows.
</ParamField>

<ParamField body="theme" type="object">
  Optional per-request theme tokens for the hosted page wrapper. For full CSS control — including card data text colors and fonts — set persistent customization via `PUT /api/card-issuer/pse-customization` (see [Custom CSS](#custom-css) below). All `theme` fields are optional.
</ParamField>

<ParamField body="theme.accent_color" type="string">
  Primary brand color (hex). Default: `#813AE3`
</ParamField>

<ParamField body="theme.background_color" type="string">
  Page background color (hex).
</ParamField>

<ParamField body="theme.panel_color" type="string">
  Card panel background color (hex). When this value (or `background_color`) is a dark color, the page automatically applies `color-scheme: dark` to the card data frame, rendering card digits in white.
</ParamField>

<ParamField body="theme.text_color" type="string">
  Primary text color (hex).
</ParamField>

<ParamField body="theme.muted_color" type="string">
  Secondary/muted text color (hex).
</ParamField>

<ParamField body="theme.border_radius" type="number">
  Border radius in pixels.
</ParamField>

<ParamField body="theme.font_family" type="string">
  CSS font-family string (e.g. `"Inter, sans-serif"`).
</ParamField>

<ParamField body="theme.logo_url" type="string">
  URL of your logo to display on the hosted page.
</ParamField>

<RequestExample>
  ```bash Card details only theme={null}
  curl -X POST 'https://crypto-api.yativo.com/api/v1/yativo-card/yc_01HX9KZMB3F7VNQP8R2WDGT4E5/cards/card_01HX9KZMB3F7VNQP8R2WDGT4E5/view-token' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -H 'Content-Type: application/json' \
    -d '{
      "enabled_views": ["data"]
    }'
  ```

  ```bash Card details + view PIN theme={null}
  curl -X POST 'https://crypto-api.yativo.com/api/v1/yativo-card/yc_01HX9KZMB3F7VNQP8R2WDGT4E5/cards/card_01HX9KZMB3F7VNQP8R2WDGT4E5/view-token' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -H 'Content-Type: application/json' \
    -d '{
      "enabled_views": ["data", "pin-view"]
    }'
  ```

  ```bash Card details + set/change PIN theme={null}
  curl -X POST 'https://crypto-api.yativo.com/api/v1/yativo-card/yc_01HX9KZMB3F7VNQP8R2WDGT4E5/cards/card_01HX9KZMB3F7VNQP8R2WDGT4E5/view-token' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -H 'Content-Type: application/json' \
    -d '{
      "enabled_views": ["data", "pin-set"]
    }'
  ```

  ```bash All three tabs + access code theme={null}
  curl -X POST 'https://crypto-api.yativo.com/api/v1/yativo-card/yc_01HX9KZMB3F7VNQP8R2WDGT4E5/cards/card_01HX9KZMB3F7VNQP8R2WDGT4E5/view-token' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -H 'Content-Type: application/json' \
    -d '{
      "enabled_views": ["data", "pin-view", "pin-set"],
      "access_code": "1234",
      "theme": {
        "accent_color": "#0f766e",
        "background_color": "#f5f1ea",
        "border_radius": 24,
        "font_family": "Inter, sans-serif",
        "logo_url": "https://yourapp.com/logo.png"
      }
    }'
  ```

  ```bash Dark theme (white card digits) theme={null}
  curl -X POST 'https://crypto-api.yativo.com/api/v1/yativo-card/yc_01HX9KZMB3F7VNQP8R2WDGT4E5/cards/card_01HX9KZMB3F7VNQP8R2WDGT4E5/view-token' \
    -H 'Authorization: Bearer YOUR_API_KEY' \
    -H 'Content-Type: application/json' \
    -d '{
      "enabled_views": ["data"],
      "theme": {
        "accent_color": "#a78bfa",
        "background_color": "#0f0f13",
        "panel_color": "#1c1c24",
        "text_color": "#f4f4f5",
        "muted_color": "#71717a",
        "border_radius": 16,
        "font_family": "Inter, sans-serif"
      }
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 PIN already set theme={null}
  {
    "success": true,
    "data": {
      "secure_view_url": "https://crypto-api.yativo.com/api/yativo-card/view/eyJhbGciOiJIUzI1NiIs...",
      "expires_at": "2026-03-26T12:01:00Z",
      "last_four": "4242",
      "card_type": "virtual",
      "holder_name": "Jane Doe",
      "pin_set": true,
      "enabled_views": ["data", "pin-view", "pin-set"],
      "requires_access_code": false
    }
  }
  ```

  ```json 422 PIN not set theme={null}
  {
    "success": false,
    "error_code": "PIN_NOT_SET",
    "message": "The cardholder has not set a card PIN yet. Direct them to pin_setup_url to set their PIN before accessing card details.",
    "data": {
      "pin_setup_url": "https://crypto-api.yativo.com/api/yativo-card/view/eyJhbGciOiJIUzI1NiIs...",
      "expires_at": "2026-03-26T12:01:00Z",
      "last_four": "4242",
      "card_type": "virtual",
      "holder_name": "Jane Doe",
      "pin_set": false
    }
  }
  ```
</ResponseExample>

<Accordion title="Response Type Definitions">
  ```typescript theme={null}
  interface ViewTokenSuccess {
    secure_view_url: string;          // Hosted page URL — embed this in an iframe
    expires_at: string;               // ISO 8601 — token is invalid after this
    last_four: string;                // Last 4 digits — safe to display without the iframe
    card_type: string;                // "virtual" | "physical"
    holder_name: string | null;       // Cardholder display name
    pin_set: boolean;                 // Always true when this response is returned
    enabled_views: string[];          // Which views are active — any combination of "data" | "pin-view" | "pin-set"
    requires_access_code: boolean;    // True if access_code was set; user must enter it before details are shown
  }

  interface ViewTokenPinNotSet {      // Returned when pin_set is false AND enabled_views does NOT include "pin-set" (HTTP 422)
    pin_setup_url: string;            // Hosted PIN setup page — direct the cardholder here
    expires_at: string;
    last_four: string;
    card_type: string;
    holder_name: string | null;
    pin_set: false;
  }
  ```
</Accordion>

***

## Step 2 — Embed the iframe

Pass the `secure_view_url` directly as the `src` of an iframe. No SDK or additional JavaScript is required.

```html theme={null}
<iframe
  src="https://crypto-api.yativo.com/api/v1/card-view?token=eyJhbGciOiJIUzI1NiIs..."
  title="Secure Card View"
  width="420"
  height="740"
  style="border: 0; border-radius: 16px;"
  allow="clipboard-read; clipboard-write"
></iframe>
```

<Tip>
  The hosted page is fully responsive. Recommended dimensions: `420 × 740px` for card details, `420 × 520px` for PIN-only views.
</Tip>

***

## Step 3 — Backend proxy (required)

Your frontend must **never** call the Yativo API directly with your credentials. Always proxy the token request through your backend:

<Tabs>
  <Tab title="Express.js">
    ```typescript theme={null}
    import express from 'express';

    const app = express();
    app.use(express.json());

    app.post('/api/card-view-token', async (req, res) => {
      // 1. Authenticate your own user first
      const user = await authenticateRequest(req);
      if (!user) return res.status(401).json({ error: 'Unauthorized' });

      const { enabled_views, access_code, theme } = req.body;

      // 2. Validate enabled_views — only accept known values
      const VALID_VIEWS = ['data', 'pin-view', 'pin-set'];
      if (enabled_views && !enabled_views.every(v => VALID_VIEWS.includes(v))) {
        return res.status(400).json({ error: 'Invalid enabled_views' });
      }

      // 3. Look up this user's card IDs from your own database
      const { yativoCardId, cardId } = await getUserCardIds(user.id);

      // 4. Call Yativo from your backend — credentials never leave the server
      const response = await fetch(
        `https://crypto-api.yativo.com/api/v1/yativo-card/${yativoCardId}/cards/${cardId}/view-token`,
        {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${process.env.YATIVO_API_KEY}`,
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            ...(enabled_views ? { enabled_views } : {}),
            ...(access_code ? { access_code } : {}),
            ...(theme ? { theme } : {}),
          }),
        }
      );

      const payload = await response.json();

      // PIN not yet set — return the setup URL so your frontend can redirect the cardholder
      if (!response.ok && payload.error_code === 'PIN_NOT_SET') {
        return res.status(422).json({
          error_code: 'PIN_NOT_SET',
          message: payload.message,
          pin_setup_url: payload.data?.pin_setup_url,
        });
      }

      if (!response.ok) {
        return res.status(502).json({ error: 'Failed to get view token' });
      }

      // Return only the URL — do not cache it
      res.json(payload.data);
    });
    ```
  </Tab>

  <Tab title="TypeScript SDK">
    ```typescript theme={null}
    import { YativoSDK } from '@yativo/crypto-sdk';

    const sdk = new YativoSDK({
      baseURL: 'https://crypto-api.yativo.com/api/v1/',
      apiKey: process.env.YATIVO_API_KEY!,
      apiSecret: process.env.YATIVO_API_SECRET!,
    });

    // In your backend route handler:
    const result = await sdk.cards.getViewToken(
      'yc_01HX9KZMB3F7VNQP8R2WDGT4E5',   // yativoCardId
      'card_01HX9KZMB3F7VNQP8R2WDGT4E5',  // cardId
      {
        enabled_views: ['data', 'pin-view', 'pin-set'],
        theme: {
          accent_color: '#813AE3',
          border_radius: 16,
        },
      }
    );

    // Send result.data.secure_view_url to your frontend
    return res.json({ url: result.data.secure_view_url });
    ```
  </Tab>

  <Tab title="Frontend (after fetch)">
    ```html theme={null}
    <!-- Your frontend fetches the URL from your backend, then sets it on the iframe -->
    <div id="card-view-wrapper">
      <iframe id="card-iframe" title="Secure Card View" width="420" height="740"
        style="border:0;border-radius:16px;" allow="clipboard-read; clipboard-write">
      </iframe>
    </div>

    <script>
      async function showCardDetails() {
        // Fetch from YOUR backend — never call Yativo directly from the browser
        const res = await fetch('/api/card-view-token', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ enabled_views: ['data', 'pin'] }),
        });

        if (!res.ok) {
          console.error('Failed to get card view token');
          return;
        }

        const { secure_view_url } = await res.json();
        document.getElementById('card-iframe').src = secure_view_url;
      }

      showCardDetails();
    </script>
    ```
  </Tab>

  <Tab title="React">
    ```tsx theme={null}
    import { useEffect, useRef, useState } from 'react';

    interface CardViewProps {
      enabledViews?: ('data' | 'pin-view' | 'pin-set')[];
      height?: number;
    }

    export function SecureCardDisplay({ enabledViews = ['data', 'pin'], height = 740 }: CardViewProps) {
      const [url, setUrl] = useState<string | null>(null);
      const [error, setError] = useState<string | null>(null);

      useEffect(() => {
        let cancelled = false;

        async function fetchToken() {
          try {
            const res = await fetch('/api/card-view-token', {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify({ enabled_views: enabledViews }),
            });

            if (!res.ok) throw new Error('Failed to fetch token');
            const data = await res.json();
            if (!cancelled) setUrl(data.secure_view_url);
          } catch (err) {
            if (!cancelled) setError('Could not load card view. Please try again.');
          }
        }

        fetchToken();
        return () => { cancelled = true; };
      }, [enabledViews.join(',')]);

      if (error) return <p style={{ color: 'red' }}>{error}</p>;
      if (!url) return <div style={{ height, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>Loading...</div>;

      return (
        <iframe
          src={url}
          title="Secure Card View"
          width={420}
          height={height}
          style={{ border: 0, borderRadius: 16 }}
          allow="clipboard-read; clipboard-write"
        />
      );
    }
    ```
  </Tab>
</Tabs>

***

## View options reference

| Value        | Tab label    | What it renders                      |
| ------------ | ------------ | ------------------------------------ |
| `"data"`     | Card Details | Card number (PAN), CVV, expiry       |
| `"pin-view"` | View PIN     | Reveals the cardholder's current PIN |
| `"pin-set"`  | Change PIN   | Form to set or change the PIN        |

Pass an array with any combination:

* `["data"]` — card details only
* `["pin-set"]` — PIN setup only (e.g. initial onboarding)
* `["data", "pin-view"]` — card details + reveal PIN
* `["data", "pin-set"]` — card details + change PIN
* `["data", "pin-view", "pin-set"]` — all three tabs

<Note>
  `pin_set` reflects the last known state from card activity webhooks. If `pin_set` is `false` and you request `["data"]` or `["pin-view"]`, the API returns a `PIN_NOT_SET` error with a `pin_setup_url` — direct the cardholder there to set their PIN, then retry once the webhook confirms.

  Requesting `["pin-set"]` always returns a `secure_view_url` (HTTP 200) regardless of `pin_set` — use this to present the PIN setup screen without checking `pin_set` first.
</Note>

***

## Access code flow

When you pass `access_code` in the token request, `requires_access_code: true` is returned. The hosted page will prompt the user to enter that code before revealing any card details. Use this for:

* Confirming user intent before showing the full card number
* Adding a secondary verification step in high-value contexts

```bash theme={null}
# Request with access code
curl -X POST '.../view-token' \
  -d '{ "enabled_views": ["data"], "access_code": "7291" }'

# Your user will see a prompt to enter 7291 before card details are shown
```

***

## Customization

<a id="custom-css" />

The secure view page supports two levels of customization: per-request theme tokens and persistent CSS overrides.

### Theme tokens (per request)

Pass a `theme` object in the view-token request to apply brand colors, fonts, and a logo. These take effect immediately on every URL you generate.

| Field              | What it affects                                            | Default           |
| ------------------ | ---------------------------------------------------------- | ----------------- |
| `accent_color`     | Active tab, status indicators, spinner, eyebrow label      | `#0f766e`         |
| `background_color` | Page background                                            | `#f5f1ea`         |
| `panel_color`      | Hero header, tab strip, and iframe frame background        | `#fffaf7`         |
| `text_color`       | Primary text                                               | `#1e2a24`         |
| `muted_color`      | Inactive tab labels, secondary text                        | `#6b746e`         |
| `border_radius`    | All rounded corners in px (clamped 8–36)                   | `24`              |
| `font_family`      | Font for the page shell                                    | system sans-serif |
| `logo_url`         | Logo displayed in the header alongside the cardholder name | none              |

All fields are optional. Unset fields fall back to the defaults above.

```bash theme={null}
curl -X POST '.../view-token' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "enabled_views": ["data", "pin-view", "pin-set"],
    "theme": {
      "accent_color": "#6366f1",
      "background_color": "#f8f8fc",
      "panel_color": "#ffffff",
      "border_radius": 16,
      "font_family": "Inter, sans-serif",
      "logo_url": "https://yourapp.com/logo.png"
    }
  }'
```

### Custom CSS (persistent, issuer-level)

For fine-grained control over colors, typography, and spacing, you can supply raw CSS that is stored against your issuer account and applied to every secure view URL you generate.

The page has two layers — your CSS affects them differently:

**Layer 1 — Page shell** (injected directly into the page `<style>` block)

The Yativo-rendered wrapper: background, header, tabs, status bar, frame container. All standard CSS applies normally.

**Layer 2 — Card data area** (forwarded to the secure card SDK)

The card number, expiry, and CVV are rendered inside a cross-origin secure iframe. Yativo extracts a specific set of selectors from your stylesheet and forwards them to the secure card SDK, which applies them to the card data elements. Background colors, borders, label fonts, and copy-button styles apply reliably. **Card digit text color** (`color` on `.pse-card-value`) is controlled by the card network's own stylesheet — it does not update through this API alone. To enable custom digit text colors (e.g. white digits on a dark background), contact Yativo to activate program-level card data theming for your account.

<Warning>
  For security, the following patterns are stripped before storage: `@import`, external `url()` references, `expression()`, and `behavior:`. Inline data URIs for images (`data:image/png;base64,...`) are allowed.
</Warning>

#### Supported selectors

**Page shell**

| Selector                                    | What it styles                                           |
| ------------------------------------------- | -------------------------------------------------------- |
| `body`                                      | Page background                                          |
| `.panel`                                    | Outer layout wrapper (no visual style by default)        |
| `.hero`                                     | Header card (background, border, shadow)                 |
| `.eyebrow`                                  | "Secure Card View" label                                 |
| `.tab`, `.tab.active`                       | Tab strip buttons (Card Details / View PIN / Change PIN) |
| `.frame`                                    | Container around the secure card data area               |
| `.status`, `.status.ready`, `.status.error` | Status bar text                                          |
| `.footer`                                   | Countdown timer                                          |

**Card data area**

| Selector           | What it overrides                                                                                                                                         |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `.pse-container`   | Card data container (background, border, border-radius)                                                                                                   |
| `.pse-card-field`  | Individual field rows                                                                                                                                     |
| `.pse-card-label`  | Field labels ("CARD NUMBER", "EXPIRES", "CVV")                                                                                                            |
| `.pse-card-value`  | Field value layout (background, font-size, padding). Digit text color is set automatically when a dark theme is detected — see [Dark theme](#dark-theme). |
| `.pse-copy-button` | Copy-to-clipboard button (background, border, `color`)                                                                                                    |

#### Manage your customization

```bash theme={null}
# Set
curl -X PUT 'https://crypto-api.yativo.com/api/v1/card-issuer/pse-customization' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{ "custom_css": "body { background: #f0f0f5; } .hero { background: #ffffff; } ..." }'

# Retrieve
curl 'https://crypto-api.yativo.com/api/v1/card-issuer/pse-customization' \
  -H 'Authorization: Bearer YOUR_API_KEY'

# Clear (revert to defaults)
curl -X PUT 'https://crypto-api.yativo.com/api/v1/card-issuer/pse-customization' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{ "custom_css": "" }'
```

#### Example: brand colours + Inter font

```css theme={null}
/* ── Page shell ── */
body  { background: #f8f8fc; }
.hero { background: #ffffff; border-color: rgba(0,0,0,0.08); }
.tab  { color: #6b7280; background: #ffffff; border-color: rgba(0,0,0,0.08); }
.tab.active { background: #6366f1; border-color: #6366f1; color: #ffffff; }
.frame { background: #ffffff; border-color: rgba(0,0,0,0.08); }
.status.ready { color: #6366f1; }
.footer { color: #9ca3af; }

/* ── Card data area ── */
.pse-container  { background-color: #ffffff; border: 1px solid rgba(0,0,0,0.08); border-radius: 12px; }
.pse-card-field { background-color: #ffffff; border-bottom-color: rgba(0,0,0,0.06); }
.pse-card-label { color: #9ca3af; font-size: 11px; font-family: Inter, sans-serif; letter-spacing: 0.05em; text-transform: uppercase; }
.pse-card-value { font-size: 14px; font-family: Inter, sans-serif; }
.pse-copy-button { color: #374151; background: transparent; border-color: rgba(0,0,0,0.12); }
```

### Dark theme

Passing a dark color in `theme.panel_color` or `theme.background_color` is all that is needed to enable dark mode. The page wrapper measures the luminance of those colors and automatically sets `color-scheme: dark` on the card data frame — the card SDK responds by rendering digits in white. No extra flag or request parameter is required.

```bash theme={null}
# Dark panel → white digits (automatic)
curl -X POST '.../view-token' \
  -d '{
    "enabled_views": ["data"],
    "theme": {
      "panel_color": "#1c1c24",
      "background_color": "#0f0f13",
      "text_color": "#f4f4f5",
      "accent_color": "#a78bfa"
    }
  }'
```

<Note>
  If card digits still appear dark after passing dark colors, the card program may not have dark-mode digit support configured at the card network level. Contact Yativo to verify your program's configuration.
</Note>

### Known limitations

<Warning>
  **Copy button icon color cannot be changed.** The clipboard SVG inside `.pse-copy-button` has a hardcoded fill. Setting `color` on `.pse-copy-button` affects the button border and background only — not the icon itself.
</Warning>

<Warning>
  **View PIN and Change PIN are always light-mode.** These tabs are rendered entirely by the card network's secure iframe and cannot be themed via custom CSS.
</Warning>

***

## Testing

Use the test script in the backend repo to verify the full flow against your running server:

```bash theme={null}
# Basic test (picks first card on the account)
node scripts/test-card-secure-view.js <api_key> <api_secret>

# Target a specific card
node scripts/test-card-secure-view.js <api_key> <api_secret> <yativo_card_id> <card_id>

# With custom views and theme
ENABLED_VIEWS=data,pin THEME_ACCENT_COLOR=#813AE3 \
  node scripts/test-card-secure-view.js <api_key> <api_secret>
```

The script will print the `secure_view_url` and a ready-to-paste iframe snippet.

***

<CardGroup cols={2}>
  <Card title="Virtual Cards" icon="credit-card" href="/yativo-crypto/cards/virtual-cards">
    Create and manage virtual cards.
  </Card>

  <Card title="API Reference — View Token" icon="key" href="/api-reference/cards/customer-secure-view">
    Full parameter reference for the view-token endpoint.
  </Card>
</CardGroup>
