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

# Card management API

> Lifecycle endpoints for an issued card: list, get, freeze, unfreeze, lost, stolen, activate — backed by an explicit state machine.

The Card Management API is the partner-facing surface for driving an issued card through its lifecycle. Every action is a transition on a status / sub-status state machine, recorded for audit. The Card Management API is live.

<Snippet file="product-term-disambiguation.mdx" />

## Why this exists

The card itself is issued through the Visa issuance rail (see [Visa card issuance](/products/card-issuing/visa-card-issuance)). What partners need from Sumvin is a stable, auth-scoped API for the user-facing actions that follow — freezing a card the user can't find, reporting it stolen, reactivating it after a false alarm — without integrating against the card issuance partner directly. The state machine is enforced server-side, so partners can drive transitions without re-implementing validation.

<Note>
  The endpoints, validation, and audit trail described below are live. Network-side card provisioning runs through the Visa issuance partner rail (in private preview); the lifecycle layer works either way.
</Note>

## Endpoints

All routes live under `/v0/card`. Auth is the standard Juno-issued JWT (`x-juno-jwt` header). Routes accept an optional [<Tooltip headline="Stamped Mandate" tip="A signed authorisation a user grants for specific scoped actions — delivered on the wire as a PINT." cta="Glossary →" href="/glossary">Stamped Mandate</Tooltip> token](/identity/pint) — when present, the token's scopes are enforced (`sr:us:pint:cards:read` for reads, `sr:us:pint:cards:manage` for state-changing actions). All read and write endpoints require the user to be in a serviceable status (KYC verified, account active).

| Method | Path                          | Action                                       | Required scope (when PINT present) |
| ------ | ----------------------------- | -------------------------------------------- | ---------------------------------- |
| `GET`  | `/v0/card`                    | List the user's cards                        | `sr:us:pint:cards:read`            |
| `GET`  | `/v0/card/{card_id}`          | Get one card, optionally with status history | `sr:us:pint:cards:read`            |
| `POST` | `/v0/card/{card_id}/freeze`   | Freeze an active card                        | `sr:us:pint:cards:manage`          |
| `POST` | `/v0/card/{card_id}/unfreeze` | Reinstate a frozen card                      | `sr:us:pint:cards:manage`          |
| `POST` | `/v0/card/{card_id}/lost`     | Report the card lost                         | `sr:us:pint:cards:manage`          |
| `POST` | `/v0/card/{card_id}/stolen`   | Report the card stolen                       | `sr:us:pint:cards:manage`          |
| `POST` | `/v0/card/{card_id}/activate` | Activate a pending card                      | `sr:us:pint:cards:manage`          |

Card identifiers in the API are the card's `external_id` (a `card-{uuid}` string), not the internal numeric primary key.

## The state machine

A card's state is a `(status, sub_status)` pair. Reads return the current pair; lifecycle actions transition between pairs. The state machine has four top-level statuses.

| Status      | Meaning                                                                                                      |
| ----------- | ------------------------------------------------------------------------------------------------------------ |
| `pending`   | Card record exists but is not yet usable — issuance underway, awaiting activation, or KYC outstanding        |
| `active`    | Card is usable for transactions                                                                              |
| `suspended` | Card is temporarily blocked — by the user (frozen, lost), by the system (fraud watch), or by compliance      |
| `closed`    | Card is permanently terminated — expired, replaced, voluntarily closed, fraud confirmed, or compliance close |

### Sub-statuses

```text theme={null}
pending     │ issuing, activation_required, kyc_pending
active      │ verified, reinstated
suspended   │ wallet_suspended, lost, stolen, fraud_suspected, compliance_review
closed      │ expired, voluntary_close, replaced, fraud_confirmed, compliance_close
```

### User-initiated transitions

The lifecycle endpoints map to a restricted set of transitions a user (or a partner acting on their behalf) is allowed to make. System and compliance transitions exist in the underlying state machine but aren't exposed to partners.

| Endpoint         | From state                                             | To state                                                                     |
| ---------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------- |
| `POST /freeze`   | `(active, *)`                                          | `(suspended, wallet_suspended)`                                              |
| `POST /unfreeze` | `(suspended, wallet_suspended)` or `(suspended, lost)` | `(active, reinstated)`                                                       |
| `POST /lost`     | `(active, *)`                                          | `(suspended, lost)`                                                          |
| `POST /stolen`   | `(active, *)`                                          | `(suspended, stolen)`, then auto-escalates to `(suspended, fraud_suspected)` |
| `POST /activate` | `(pending, activation_required)`                       | `(active, verified)`                                                         |

<Info>
  Reporting a card stolen is a two-step transition on the server side. The card moves to `(suspended, stolen)`, then the system attempts a follow-up transition to `(suspended, fraud_suspected)`. The response always reflects the final state. If the escalation fails, the card stays at `(suspended, stolen)` and the failure is logged.
</Info>

### Discoverable next actions

Each card response includes a `_links` object with the actions available from the current state. A frozen card surfaces an `unfreeze` link; an active card surfaces `freeze`, `lost`, and `stolen` links; a pending card awaiting activation surfaces an `activate` link. Drive UI off these links rather than computing legality client-side.

## Reference

### List cards

[`GET /v0/card/`](/api-reference/cards/list-cards)

Returns every card belonging to the authenticated user, with current status and the linked funding wallet address inlined for each card.

```bash theme={null}
curl https://api.sumvin.com/v0/card \
  -H "x-juno-jwt: <token>" \
  -H "x-juno-orgid: <your-org-id>"
```

**Response** — `200 OK`

```json theme={null}
{
  "cards": [
    {
      "id": "card-3f9c…",
      "last_four": "4242",
      "brand": "visa",
      "exp_month": 12,
      "exp_year": 2028,
      "cardholder_name": "Ada Lovelace",
      "card_type": "virtual",
      "is_primary": true,
      "linked_wallet_address": "0xE23c…E992",
      "created_at": 1704067200000,
      "current_status": {
        "status": "active",
        "sub_status": "verified",
        "changed_by": "system",
        "created_at": 1704067200000
      }
    }
  ],
  "total": 1,
  "_links": {
    "self": { "href": "/v0/card" },
    "user": { "href": "/v0/user/me" }
  }
}
```

### Get card

[`GET /v0/card/{card_id}`](/api-reference/cards/get-card)

Returns a single card by its `external_id`. Pass `include_history=true` to inline the full status change history.

<ParamField path="card_id" type="string" required>
  The card's external identifier (`card-{uuid}`).
</ParamField>

<ParamField query="include_history" type="boolean" default="false">
  When `true`, returns the full ordered status history (most recent first) on `status_history`.
</ParamField>

The response includes:

<ResponseField name="id" type="string">External card ID.</ResponseField>
<ResponseField name="last_four" type="string">Last four digits of the PAN.</ResponseField>
<ResponseField name="brand" type="string">Card brand — one of `sumvin`, `visa`, `mastercard`, `amex`, `discover`, `unknown`.</ResponseField>
<ResponseField name="exp_month" type="integer">Expiry month (1–12).</ResponseField>
<ResponseField name="exp_year" type="integer">Expiry year (4 digits).</ResponseField>
<ResponseField name="cardholder_name" type="string | null">Cardholder name as printed.</ResponseField>
<ResponseField name="card_type" type="string">One of `virtual`, `physical`, `metal`.</ResponseField>
<ResponseField name="is_primary" type="boolean">Whether this is the user's primary card.</ResponseField>
<ResponseField name="linked_wallet_address" type="string | null">Address of the [funding wallet](/products/card-issuing/funding-wallets) backing this card.</ResponseField>
<ResponseField name="created_at" type="integer">Creation timestamp (epoch ms).</ResponseField>
<ResponseField name="current_status" type="object">Current `(status, sub_status, changed_by, created_at)` pair.</ResponseField>
<ResponseField name="status_history" type="array | null">Full status history when `include_history=true`.</ResponseField>

<Note>
  The PAN, CVV, and other sensitive card data are never returned by the Card Management API. Surfacing the full PAN is part of the issuance flow and lives behind a separate, secured surface.
</Note>

### Freeze

[`POST /v0/card/{card_id}/freeze`](/api-reference/cards/freeze-card)

Suspends an active card. Transitions the card to `(suspended, wallet_suspended)`. Reversible via `unfreeze`.

```bash theme={null}
curl -X POST https://api.sumvin.com/v0/card/card-3f9c…/freeze \
  -H "x-juno-jwt: <token>" \
  -H "x-juno-orgid: <your-org-id>"
```

**Response** — `200 OK`

```json theme={null}
{
  "id": "card-3f9c…",
  "last_four": "4242",
  "brand": "visa",
  "card_type": "virtual",
  "is_primary": true,
  "created_at": 1704067200000,
  "previous_status": { "status": "active", "sub_status": "verified", "changed_by": "system", "created_at": 1704067200000 },
  "current_status":  { "status": "suspended", "sub_status": "wallet_suspended", "changed_by": "self", "created_at": 1704153600000 },
  "action": "freeze",
  "_links": {
    "self": { "href": "/v0/card/card-3f9c…" },
    "unfreeze": { "href": "/v0/card/card-3f9c…/unfreeze", "method": "POST" }
  }
}
```

### Unfreeze

[`POST /v0/card/{card_id}/unfreeze`](/api-reference/cards/unfreeze-card)

Reinstates a card from `(suspended, wallet_suspended)` or `(suspended, lost)` to `(active, reinstated)`. Returns the same `CardActionResponse` shape as `freeze`.

### Report lost

[`POST /v0/card/{card_id}/lost`](/api-reference/cards/report-lost-card)

Transitions an active card to `(suspended, lost)`. The card can be reinstated with `unfreeze` if the user finds it, or replaced through the issuance rail.

### Report stolen

[`POST /v0/card/{card_id}/stolen`](/api-reference/cards/report-stolen-card)

Transitions an active card to `(suspended, stolen)` and then auto-escalates to `(suspended, fraud_suspected)`. This action is intentionally less recoverable than `lost` — reinstating from a stolen state requires going through fraud review, not a simple unfreeze.

### Activate

[`POST /v0/card/{card_id}/activate`](/api-reference/cards/activate-card)

Activates a card sitting at `(pending, activation_required)`, transitioning it to `(active, verified)`. Use this when the issuance rail has provisioned the card and the user has confirmed receipt.

## Errors

All errors follow [RFC 7807 Problem Details](/error-handling).

| Code          | Status | Meaning                                                                                        |
| ------------- | ------ | ---------------------------------------------------------------------------------------------- |
| `CRD-404-001` | 404    | Card not found                                                                                 |
| `CRD-404-002` | 404    | No status record exists for this card (internal — should not occur for healthy cards)          |
| `CRD-403-001` | 403    | Card belongs to a different user                                                               |
| `CRD-403-002` | 403    | The transition is not allowed for the user (e.g. trying to activate a card that isn't pending) |
| `CRD-400-001` | 400    | The requested transition is not valid from the current state                                   |

A typical error response:

```json theme={null}
{
  "type": "https://api.sumvin.com/errors/crd-403-002",
  "title": "Action Not Allowed",
  "status": 403,
  "detail": "This action is not allowed for the current card status",
  "instance": "/v0/card/card-3f9c…/unfreeze",
  "error_code": "CRD-403-002"
}
```

## Related

* [Card issuing overview](/products/card-issuing/overview)
* [Funding wallets](/products/card-issuing/funding-wallets) — what backs card spend
* [Card issuing concept](/concepts/card-issuing) — the model behind the API
* [Safes and identity](/concepts/safes-and-identity) — the on-chain identity anchor
* [PAN (glossary)](/glossary)
* [Issue a card quickstart](/guides/quickstarts/issue-card)
* [Crypto wallet card use case](/overview/use-cases/crypto-wallet-card)
