ROOKDocs
5 min readUpdated August 2026

Secure PCI Card Display

Show PAN, expiry, and CVV in a PCI iframe. Do not put those values in your page DOM, logs, or JSON APIs unless the caller is in PCI scope.

When to use which operation

Goal Call
Display PAN / expiry / CVV in a web or mobile WebView POST /v1/cards/{card_id}/embed-sessions, then load embed_url in an iframe
Receive PAN / expiry / CVV as JSON POST /v1/cards/{card_id}/reveal: PCI DSS compliant callers only
Look up a card you already hold the PAN for POST /v1/cards/search-by-pan: request body includes pan; the card in the response does not

GET /v1/cards, GET /v1/cards/{card_id}, and every other card object return last_four, exp_month, and exp_year only.

Embed flow

  1. From your backend, POST /v1/cards/{card_id}/embed-sessions with Authorization and Idempotency-Key. The response is:

    {
      "object": "card_embed_session",
      "card_id": "0c4e8f16-2a7b-4d93-b5e1-8f3a6c9d0142",
      "session_token": "8f2b1c400c1a4b7e9a3d6d5f2e1a7b90",
      "expires_at": "2026-08-27T15:09:05Z",
      "embed_url": "https://api.rookpayments.com/v1/cards/embed?session_token=8f2b1c400c1a4b7e9a3d6d5f2e1a7b90"
    }

    This JSON never includes PAN or CVV. Treat session_token as a secret until expires_at.

  2. Pass embed_url to the client. Do not proxy the HTML through your origin.

  3. Render an iframe whose src is embed_url:

    <iframe
      title="Card number"
      src="https://api.rookpayments.com/v1/cards/embed?session_token=8f2b1c400c1a4b7e9a3d6d5f2e1a7b90"
      sandbox="allow-scripts"
      referrerpolicy="no-referrer"
      width="320"
      height="160">
    </iframe>
  4. GET /v1/cards/embed authenticates with session_token only. Do not send Authorization or X-Program-ID. An expired or unknown token returns 401.

Create a new session when the token expires or when the cardholder opens the display again. Do not reuse a token after expires_at.

Content-Security-Policy

On your page that hosts the iframe, allow Rook as a frame source. A minimal directive:

Content-Security-Policy: frame-src https://api.rookpayments.com https://api.sandbox.rookpayments.com

If you already set frame-src or default-src, add those hosts rather than replacing the rest of the policy. frame-ancestors on your page does not control this iframe; frame-src does.

The embed document itself is served with a restrictive CSP (default-src 'none' plus the styles needed to render the PAN). Do not expect to inject scripts into the iframe. sandbox="allow-scripts" is enough for the page to render; omit allow-same-origin so the parent cannot read the iframe DOM.

Iframe usage

  • Use HTTPS parents only. Mixed content blocks the iframe.
  • Size the iframe so PAN, expiry, and CVV are visible without the parent overlaying them.
  • Do not copy iframe contents with contentDocument or window.frames. Cross-origin access is blocked; do not work around it.
  • For copy-to-clipboard, keep a control in the iframe (served by Rook) rather than reading PAN in the parent.
  • Mobile WebViews: enable third-party cookies only if your WebView otherwise strips the query string; session_token is in the URL, so cookies are not required.

Sandbox embed_url hosts use https://api.sandbox.rookpayments.com. Production uses https://api.rookpayments.com. Match frame-src to the environment.

Reveal JSON (PCI callers)

POST /v1/cards/{card_id}/reveal returns:

{
  "object": "card_reveal",
  "card_id": "0c4e8f16-2a7b-4d93-b5e1-8f3a6c9d0142",
  "pan": "4111111111111111",
  "exp_month": 8,
  "exp_year": 2029,
  "cvv": "123"
}

Requirements:

  • The API key must be enabled for PAN reveal. Other keys receive 403 with code permission_denied.
  • The caller must be PCI DSS compliant (SAQ D or a listed Level 1 processor) and must handle PAN only inside a PCI environment.
  • Do not log the response body, write it to analytics, or send it to a browser that is not in PCI scope.
  • Prefer the embed iframe for cardholder display.

Search-by-PAN (POST /v1/cards/search-by-pan) accepts pan in the request and returns a card without PAN or CVV. Handle the request body under PCI DSS the same way you handle any PAN you already store.

Was this page helpful?