Secure 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
contentDocumentorwindow.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_tokenis in the URL, so cookies are not required.
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
403withcodepermission_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.
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.