Skip to main content
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.

Why this exists

The card itself is issued through an external rail (see Baanx 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 issuer directly. The state machine is enforced server-side, so partners can drive transitions without re-implementing validation.
The endpoints, validation, and audit trail described below are shipped. The underlying issued card may be a stub today depending on integration status with the issuance rail — the lifecycle layer works either way.

Endpoints

All routes live under /v0/cards. Auth is the standard Juno-issued JWT (x-juno-jwt header). Routes accept an optional token — 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).
MethodPathActionRequired scope (when PINT present)
GET/v0/cardsList the user’s cardssr:us:pint:cards:read
GET/v0/cards/{card_id}Get one card, optionally with status historysr:us:pint:cards:read
POST/v0/cards/{card_id}/freezeFreeze an active cardsr:us:pint:cards:manage
POST/v0/cards/{card_id}/unfreezeReinstate a frozen cardsr:us:pint:cards:manage
POST/v0/cards/{card_id}/lostReport the card lostsr:us:pint:cards:manage
POST/v0/cards/{card_id}/stolenReport the card stolensr:us:pint:cards:manage
POST/v0/cards/{card_id}/activateActivate a pending cardsr: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.
StatusMeaning
pendingCard record exists but is not yet usable — issuing in progress, awaiting activation, or KYC outstanding
activeCard is usable for transactions
suspendedCard is temporarily blocked — by the user (frozen, lost), by the system (fraud watch), or by compliance
closedCard is permanently terminated — expired, replaced, voluntarily closed, fraud confirmed, or compliance close

Sub-statuses

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.
EndpointFrom stateTo 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)
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.

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/cards/ Returns every card belonging to the authenticated user, with current status and the linked funding wallet address inlined for each card.
curl https://api.sumvin.com/v0/cards \
  -H "x-juno-jwt: <token>" \
  -H "x-juno-orgid: <your-org-id>"
Response200 OK
{
  "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/cards" },
    "user": { "href": "/v0/user/me" }
  }
}

Get card

GET /v0/cards/{card_id} Returns a single card by its external_id. Pass include_history=true to inline the full status change history.
card_id
string
required
The card’s external identifier (card-{uuid}).
include_history
boolean
default:"false"
When true, returns the full ordered status history (most recent first) on status_history.
The response includes:
id
string
External card ID.
last_four
string
Last four digits of the PAN.
brand
string
Card brand — one of sumvin, visa, mastercard, amex, discover, unknown.
exp_month
integer
Expiry month (1–12).
exp_year
integer
Expiry year (4 digits).
cardholder_name
string | null
Cardholder name as printed.
card_type
string
One of virtual, physical, metal.
is_primary
boolean
Whether this is the user’s primary card.
linked_wallet_address
string | null
Address of the funding wallet backing this card.
created_at
integer
Creation timestamp (epoch ms).
current_status
object
Current (status, sub_status, changed_by, created_at) pair.
status_history
array | null
Full status history when include_history=true.
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.

Freeze

POST /v0/cards/{card_id}/freeze Suspends an active card. Transitions the card to (suspended, wallet_suspended). Reversible via unfreeze.
curl -X POST https://api.sumvin.com/v0/cards/card-3f9c…/freeze \
  -H "x-juno-jwt: <token>" \
  -H "x-juno-orgid: <your-org-id>"
Response200 OK
{
  "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/cards/card-3f9c…" },
    "unfreeze": { "href": "/v0/cards/card-3f9c…/unfreeze", "method": "POST" }
  }
}

Unfreeze

POST /v0/cards/{card_id}/unfreeze Reinstates a card from (suspended, wallet_suspended) or (suspended, lost) to (active, reinstated). Returns the same CardActionResponse shape as freeze.

Report lost

POST /v0/cards/{card_id}/lost 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/cards/{card_id}/stolen 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/cards/{card_id}/activate 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.
CodeStatusMeaning
CRD-404-001404Card not found
CRD-404-002404No status record exists for this card (internal — should not occur for healthy cards)
CRD-403-001403Card belongs to a different user
CRD-403-002403The transition is not allowed for the user (e.g. trying to activate a card that isn’t pending)
CRD-400-001400The requested transition is not valid from the current state
A typical error response:
{
  "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/cards/card-3f9c…/unfreeze",
  "error_code": "CRD-403-002"
}