Skip to main content

Security & Attestation

LedgerOS is designed for AI agents that spend real money. This guide covers the security model that makes this safe.

The Problem with AI + Payments

When AI agents can spend money, you need answers to:
  • What did the agent claim it was doing? (audit trail)
  • Did the transaction match the claim? (verification)
  • Who accessed the card credentials? (accountability)
  • Can we limit blast radius? (controls)
LedgerOS addresses these with attestation, intents, and merchant-locking.

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.

Retrieving Credentials

Request credentials by providing an attestation:
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",
  "pan": "4111111111111111",
  "cvv": "123",
  "expirationMonth": "12",
  "expirationYear": "2025",
  "last4": "1111"
}
The accessEventId links this credential access to your attestation for audit purposes.

Credential Exposure Modes

When creating a card, you can control credential access:
ModeDescription
rawPan (default)Returns full credentials in API response
neverCredentials cannot be retrieved at all
Creating a card that prevents credential access:
curl -X POST https://api.ledger.so/v1/agents/$AGENT_ID/cards \
  -H "Api-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "purpose": "Hosted checkout only",
    "type": "single",
    "maxAmount": 10000,
    "credentialExposure": "never"
  }'

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

Layered Security Model

LedgerOS provides multiple layers of protection:
LayerWhat it doesExample
AttestationRecords what agent claimed”Ordering lunch on DoorDash for $25”
IntentsMatches transactions to claimsTransaction $24.50 at DoorDash → matched
Merchant-lockingRestricts where card worksCard locked to DoorDash, declined at Amazon
Spending limitsCaps total spendAgent limited to $500/month
Time controlsRestricts when card worksOnly active 9am-5pm weekdays
For maximum security, use all layers together:
{
  "purpose": "DoorDash lunch orders",
  "type": "multi",
  "limits": {
    "perAuth": 5000,
    "perMonth": 50000
  },
  "requireAttestation": true,
  "activeHoursStart": 11,
  "activeHoursEnd": 14,
  "activeTimezone": "America/New_York",
  "activeDays": [1, 2, 3, 4, 5]
}
This card:
  • Locks to first merchant (DoorDash)
  • Max 50/transaction,50/transaction, 500/month (via limits object)
  • Requires attestation before each use
  • Only works 11am-2pm Mon-Fri Eastern

Best Practices

Include expected amount, merchant, and reason. This helps with audit trails and fraud detection.
Never expose API keys in client-side code. All credential access should happen server-side.
Set up webhook handlers for spend.unattested events to detect anomalies.
If users complete checkout on a hosted payment page, disable credential access entirely.
When an agent needs to buy from a new merchant, use a single-use card that auto-closes after the transaction.
Subscribe to intent.mismatched webhooks to catch when transactions deviate significantly from declared intent.