Skip to content

See a card's details

People see a card’s number, expiry and CVC on screen, inside secure card fields. Your code or an agent can read an active virtual card’s details through the API, but only if the account’s owner allows it and only with a key made for it.

In the dashboard, open a card and choose Show details (number, expiry, CVC) or, for a physical card, Show PIN. In the app, open the card and confirm with Face ID.

Rule
Fresh second factorConfirm it’s you if you haven’t in the last 10 minutes: authenticator code in the dashboard, Face ID in the app
Who sees which cardAn owner or admin: any of the account’s cards. Anyone else: only a physical card issued to them
Active cards onlyA physical card shows details once it has arrived and been activated. Only a physical card has a PIN
Inside secure framesThe card processor’s secure elements draw the details in frames on the page. They never pass through Tuppence’s servers or the page’s own code, so they can’t reach a log. Copy buttons copy each field
Hides itselfAfter a minute, or as soon as you leave the page or app. Hide hides them sooner
AuditedEach view is logged as card.details_viewed: who, which card, when. Never the details
RateFive looks a minute, then wait a moment and try again

To pay at a web checkout, code or an agent needs the number, CVC and expiry for the merchant’s payment form. GET /v1/cards/{id}/details returns them for an active virtual card: your business’s own, or an agent’s. A request an agent signs reads only that agent’s own cards. This is the one place card numbers leave the card processor through Tuppence, so every condition below must hold.

OwnerCode or agentTuppenceMerchant checkoutallow (fresh 2FA)GET …/details (rk_…)flag, opt-in, scope, cardcard.details_retrievednumber, cvc (no-store)payment form only

Only the account’s owner can do this. In the dashboard, go to Settings → Cards and switch on Card numbers through the API. It asks for a fresh second factor and is recorded in the audit log. Switching it off stops every read at once.

2. Create a restricted key with card_details:read

Section titled “2. Create a restricted key with card_details:read”

Create a restricted key (rk_…, in Developers → API keys → Restricted) with the card_details:read scope. Give it to the code or agent that pays, with no other scopes it doesn’t need.

card_details:read is never implied. A secret key doesn’t have it, no team role has it, and a key with every other scope still doesn’t. A key has it only when made with it by name.

The safest card to read is a single-use card locked to the merchant (create_agent_card over MCP, or POST /v1/cards with a merchant). It pays that merchant once, up to that amount, and nothing else.

Terminal window
curl https://api.tuppence.ai/v1/cards/card_…/details \
-H "Authorization: Bearer $TUPPENCE_CARD_DETAILS_KEY"
{
"object": "card_details",
"card": "card_…",
"number": "4000 00…",
"cvc": "•••",
"exp_month": 12,
"exp_year": 2029,
"livemode": false
}
  • number is the whole card number, with no spaces.
  • The response has Cache-Control: no-store. The SDKs send no idempotency key with it, cache nothing and log nothing.
  • Over MCP, the same read is the get_card_details tool.

The number and CVC are all anyone needs to spend on the card. Use them for one checkout, then let them go.

  • Never store, print or log them: not in a database, file, cache, prompt, transcript, error report or debug line. Read them again for the next checkout.
  • Send them only to the merchant’s payment form. Never to another tool, another agent, or a person in chat.
  • 3-D Secure goes to the cardholder. If the checkout asks for a code sent to a phone, that code goes to the person the card is issued to. Stop and ask a person; don’t try to get past it. See 3-D Secure.
Guardrail
Restricted key with card_details:readNever a secret key, a dashboard or app session, or a team role
Owner opt-inPer account, by its owner, with a fresh second factor; audited
Platform flagagent_card_details, off in production until PCI DSS service-provider compliance is confirmed
A virtual card, activeYour business’s own or an agent’s. A physical card is refused (its person sees it in the dashboard or app), as is a frozen, cancelled or not yet activated card
One agent, its own cardsA request signed by an agent (agent identity) reads only that agent’s cards: not another agent’s, and not your business’s own
Rate limitedTen reads a minute per key: one checkout at a time
Audited and evented, without the numberEvery read is in the audit log (card.details_retrieved) and sent as a webhook event. Neither carries the number or CVC
Never in a logno-store; not kept in your request logs or with an idempotency key; the number is stored nowhere

card.details_retrieved is sent on every read, so the business sees each one and can alert on any it doesn’t expect:

{
"object": "card_details_retrieval",
"card": "card_…",
"agent": "agt_…",
"api_key": "rk_…",
"retrieved_at": 1791374400,
"livemode": false
}

agent is the card’s agent, or null for your business’s own card. api_key is the key’s id, not its secret. See Webhooks.

WhereAnswer
Local development and CI (the simulated gateway)A generated test number: card-shaped, passes the Luhn check, can’t pay anywhere
Test mode on Tuppence’s serversNo number exists for test cards: 409 card_details_unavailable. Build and test against your local setup
CodeHTTPMeans
permission_denied403Not a restricted key with card_details:read (a secret key never has it), or the card is physical, or an agent signed the request and the card isn’t its own
insufficient_role403A dashboard or app session. People see card details in the secure frames, not through the API
feature_not_enabled403The owner hasn’t switched it on (Settings → Cards), or the platform flag is off here (production, for now)
invalid_state400The card isn’t active: frozen, cancelled or not yet activated
resource_missing404No such card in this account and mode
card_details_unavailable409No number exists for this card: a test card outside local development
rate_limited429More than ten reads a minute with this key. Wait for Retry-After

Docs assistant

Open as a page
Loading the assistant…