Skip to main content

Credential Access

This guide explains how to securely retrieve card credentials (PAN, CVV, expiration) using the attestation model.

Attestation Model

LedgerOS uses an attestation-before-access model. Before retrieving card credentials, your agent must attest what it intends to do with the card. This creates an audit trail and enables automatic policy enforcement.

Exposure Modes

When creating a card, you specify how credentials can be accessed:
ModeRisk LevelUse Case
neverLowestHosted checkout only
extensionOnlyMediumBrowser automation with extension
rawPanHighestDirect API access (testing, trusted environments)

Extension Only (Default)

Returns a short-lived token that only the browser extension can redeem:
curl -X POST https://api.ledger.so/v1/cards/$CARD_ID/details \
  -H "Api-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "summary": "Order dinner on DoorDash",
    "expectedAmount": 2500,
    "merchantText": "DoorDash"
  }'
Response:
{
  "accessEventId": "evt_abc123",
  "exposureMode": "extensionOnly",
  "detailsToken": "tok_xyz789...",
  "tokenExpiresAt": 1703520120000,
  "last4": "4242"
}

Raw PAN

Returns credentials directly in the response. Requires explicit acknowledgment:
curl -X POST https://api.ledger.so/v1/cards/$CARD_ID/details \
  -H "Api-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "summary": "Test purchase",
    "expectedAmount": 1000
  }'
Response:
{
  "accessEventId": "evt_abc123",
  "exposureMode": "rawPan",
  "pan": "4111111111111111",
  "cvv": "123",
  "expirationMonth": "12",
  "expirationYear": "2025",
  "last4": "1111"
}
Raw PAN mode exposes credentials in the API response. Only use this for testing or in highly trusted environments. The card must be created with credentialExposure: "rawPan" and acknowledgeRawPanRisk: true.

Attestation Payload

The attestation payload describes the intended use:
FieldTypeRequiredDescription
summarystringYesHuman-readable description of intent
expectedAmountnumberNoExpected transaction amount in cents
expectedCurrencystringNoCurrency code (default: USD)
merchantTextstringNoExpected merchant name
reasonstringNoone-time, subscription, preauth
clientContextobjectNoCustom metadata for your records
Example with full attestation:
{
  "summary": "Subscribe to Netflix Standard plan",
  "expectedAmount": 1549,
  "expectedCurrency": "USD",
  "merchantText": "Netflix",
  "reason": "subscription",
  "clientContext": {
    "sessionId": "sess_123",
    "toolName": "subscription-agent",
    "userRequest": "Set up Netflix subscription"
  }
}

Transaction Correlation

When a transaction occurs, it’s automatically correlated with recent credential access events:
StatusMeaning
attestedCredential access found within the attestation window
unattestedNo recent credential access found
staleCredential access found but outside the window
The default attestation window is 10 minutes. Configure via attestationWindowMinutes when creating the card (1-60 minutes).

Rate Limits

  • Maximum 10 credential retrievals per hour per card
  • Tokens expire after 120 seconds

Using the Browser Extension

For extensionOnly mode, use the Ledger browser extension:
// In your automation script
const { fillCardForm } = require('@ledger/extension-sdk');

// Get the token from API
const response = await fetch(`/v1/cards/${cardId}/details`, {
  method: 'POST',
  headers: { 'Api-Key': apiKey },
  body: JSON.stringify({ summary: 'Purchase item' })
});
const { detailsToken } = await response.json();

// Extension fills the form (PAN never exposed to your code)
await fillCardForm(page, detailsToken, {
  pan: '#card-number',
  cvv: '#cvv',
  expMonth: '#exp-month',
  expYear: '#exp-year'
});

Best Practices

Include expected amount, merchant, and reason. This helps with audit trails and fraud detection.
Keep PAN out of your application code. The extension handles credentials securely.
Tokens expire in 120 seconds. Request a new token if the checkout takes longer.
Set up webhook handlers for spend.unattested events to detect anomalies.