> ## Documentation Index
> Fetch the complete documentation index at: https://vetta.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Cards

> Prepaid virtual cards an identity buys with organization credits, with a single audited credential reveal.

Every route is nested under an identity. `{identity}` accepts an `idn_…` id or the identity's name.
An identity may hold any number of cards. A card is `prepaid`: bought once for `load_micro_usd`,
never reloaded. Amounts are integer micro-USD (`1 USD = 1000000`). An agent reaches a card through
the identity its session selected; there is no card-to-agent assignment to manage.

<Note>On a deployment with no card issuer bound, quoting, issuing, retrying and revealing answer `501 feature_not_configured` before any credit moves; reads return what the identity already holds.</Note>

## The card object

```json theme={"system"}
{
  "id": "vcd_01j9x2k3m4n5p6q7r8s9t0v1w2",
  "object": "card",
  "identity_id": "idn_01j9x2k3m4n5p6q7r8s9t0v1w2",
  "kind": "prepaid",
  "status": "active",
  "label": "Ads",
  "brand": "Visa",
  "last4": "4242",
  "load_micro_usd": 50000000,
  "spent_micro_usd": 1299000,
  "currency": "usd",
  "expires_at": "2027-09-01T00:00:00Z",
  "failure_reason": null,
  "created_at": "2026-09-01T10:00:00Z",
  "updated_at": "2026-09-01T10:00:04Z"
}
```

`status` is one of `pending_payment`, `issuing`, `active`, `failed`, `cancelled`, `refunded`. This
object never carries a number, PIN, CVV or redemption link — those live only in the
[credential reveal](#reveal-credentials).

## List cards

`GET /v1/identities/{identity}/cards` — scope `agents:read`

A [paginated list](/docs/api/pagination) of `card` objects. Filter with `?status=active`.

## Price a load

`POST /v1/identities/{identity}/cards/quote` — scope `agents:read`

```json theme={"system"}
{ "load_micro_usd": 50000000 }
```

```json theme={"system"}
{
  "object": "card_quote",
  "load_micro_usd": 50000000,
  "price_micro_usd": 50500000,
  "brand": "Visa",
  "min_load_micro_usd": 1000000,
  "max_load_micro_usd": 150000000
}
```

`price_micro_usd` is what issuing will debit from credits — the load plus the issuer's fee. A load
outside the bounds answers `validation_failed`. Zero side effects.

## Issue a card

`POST /v1/identities/{identity}/cards` — scope `billing:write`

```json theme={"system"}
{ "label": "Ads", "load_micro_usd": 50000000 }
```

The price is debited from the organization's credits **before** the issuer is asked; a short
balance answers `402 insufficient_credits` and nothing is ordered. Answers `201` with the card in
status `issuing` (the issuer is still fulfilling; read it again) or `active`. If the issuer refuses
the order the card is `failed` with a provider-neutral `failure_reason`, its debit is held, and
[retry](#lifecycle) or [refund](#lifecycle) settles it. Honours `Idempotency-Key`; the same key
replays the same card without a second debit.

## Read a card

`GET /v1/identities/{identity}/cards/{card}` — scope `agents:read`

Reading an `issuing` card reconciles it with the issuer, so the status you see is current.

## Reveal credentials

`GET /v1/identities/{identity}/cards/{card}/credentials` — scope `billing:write`

```json theme={"system"}
{
  "object": "card_credentials",
  "card_id": "vcd_01j9x2k3m4n5p6q7r8s9t0v1w2",
  "number": "4242424242424242",
  "pin": "1234",
  "cvv": null,
  "expiry": "09/27",
  "url": "https://redeem.example/abc",
  "instructions": "Enter the number and PIN at checkout.",
  "revealed_at": "2026-09-01T10:05:00Z"
}
```

The only route that answers a `card_credentials` object. Every reveal writes an audit event
(`card.credentials_revealed` — the event names the card, never the credential) and the response is
`Cache-Control: no-store`. A card that is not `active`, or that the caller cannot see, answers
`404 not_found` — never a `403` that confirms it exists.

## Lifecycle

`POST /v1/identities/{identity}/cards/{card}/retry` — scope `billing:write` — re-runs issuance from `failed`, reusing the held debit.

`POST /v1/identities/{identity}/cards/{card}/cancel` — scope `billing:write` — marks an `active` card `cancelled`. The prepaid balance stays on the card.

`POST /v1/identities/{identity}/cards/{card}/refund` — scope `billing:write` — returns the credits of a `pending_payment` or `failed` card and marks it `refunded`.

A lifecycle action from any other status answers `409 state_conflict`. A prepaid card has no
top-up: buy another card.

## Transactions

`GET /v1/identities/{identity}/cards/{card}/transactions` — scope `agents:read`

`POST /v1/identities/{identity}/cards/{card}/transactions` — scope `sessions:write`

```json theme={"system"}
{ "amount_micro_usd": 1299000, "merchant": "Example Ads", "description": "Campaign #4" }
```

```json theme={"system"}
{
  "id": "vct_01j9x2k3m4n5p6q7r8s9t0v1w3",
  "object": "card_transaction",
  "card_id": "vcd_01j9x2k3m4n5p6q7r8s9t0v1w2",
  "amount_micro_usd": 1299000,
  "merchant": "Example Ads",
  "description": "Campaign #4",
  "session_id": "ses_01j9x2k3m4n5p6q7r8s9t0v1w4",
  "metadata": {},
  "created_at": "2026-09-01T11:00:00Z"
}
```

Logging a spend increments `spent_micro_usd` and is refused (`validation_failed`) when it would
exceed the load. A prepaid card has no live authorization feed; this ledger is what the identity
and its agents record. The list is paginated (`limit` default 50, max 200).

## Errors

`insufficient_credits` before any issuer call; `validation_failed` for a load outside the quote's
bounds or an over-load spend; `state_conflict` for a lifecycle action from the wrong status;
`not_found` for the credentials of a card that is not active; `feature_not_configured`. See
[Errors](/docs/api/errors).
