Skip to main content

Cards

Overview

The API provides virtual card issuance tied to authenticated users. Each card has a full lifecycle managed through a status state machine, with support for freezing, reporting lost or stolen cards, and configuring funding sources. Cards are:
  • User-scoped — all card operations derive the user from the authentication token
  • Status-tracked — every status change is recorded as an auditable event with who initiated it
  • Wallet-linked — cards can be linked to a user’s wallet for funding
  • Hypermedia-driven — responses include _links that reflect available actions based on the card’s current status

Quick Start

1. List your cards

curl /v0/cards/ \
  -H "x-juno-jwt: <token>"
Response: 200 OK
{
  "cards": [
    {
      "id": 42,
      "last_four": "7890",
      "brand": "visa",
      "exp_month": 3,
      "exp_year": 2027,
      "cardholder_name": "Alex Rivera",
      "card_type": "virtual",
      "is_primary": true,
      "linked_wallet_id": 15,
      "created_at": 1700000000000,
      "current_status": {
        "id": 108,
        "card_id": 42,
        "status": "active",
        "sub_status": "verified",
        "changed_by": "self",
        "created_at": 1700000060000
      }
    }
  ],
  "total": 1,
  "_links": {
    "self": { "href": "/v0/cards" },
    "user": { "href": "/v0/user/me" }
  }
}

2. Get a specific card

curl /v0/cards/42 \
  -H "x-juno-jwt: <token>"
The response includes _links that show which actions are available based on the card’s current status. An active card will include links to freeze, report lost, and report stolen.

3. Freeze the card

curl -X POST /v0/cards/42/freeze \
  -H "x-juno-jwt: <token>"
Response: 200 OK
{
  "id": 42,
  "last_four": "7890",
  "brand": "visa",
  "card_type": "virtual",
  "is_primary": true,
  "created_at": 1700000000000,
  "previous_status": {
    "id": 108,
    "card_id": 42,
    "status": "active",
    "sub_status": "verified",
    "changed_by": "self",
    "created_at": 1700000060000
  },
  "current_status": {
    "id": 109,
    "card_id": 42,
    "status": "suspended",
    "sub_status": "wallet_suspended",
    "changed_by": "self",
    "created_at": 1700000120000
  },
  "action": "freeze",
  "_links": {
    "self": { "href": "/v0/cards/42" },
    "unfreeze": { "href": "/v0/cards/42/unfreeze", "method": "POST" },
    "funding": { "href": "/v0/cards/42/funding" }
  }
}

4. Unfreeze the card

curl -X POST /v0/cards/42/unfreeze \
  -H "x-juno-jwt: <token>"
The card returns to active/reinstated status. All action responses include both previous_status and current_status so your UI can show the transition.

Card Types

TypeValueDescription
VirtualvirtualDigital card for online transactions
PhysicalphysicalMailed plastic card
MetalmetalPremium metal card

Card Statuses

Cards use a two-level status system: a primary status and a sub_status that provides detail about why the card is in that state.

Primary Statuses

StatusMeaning
pendingCard has been created but is not yet usable
activeCard is live and can process transactions
suspendedCard is temporarily blocked — may be unfrozen depending on the reason
closedCard is permanently deactivated

Sub-Statuses by Primary Status

Pending:
Sub-StatusDescription
issuingCard is being provisioned by the processor
activation_requiredCard is ready and waiting for user activation
kyc_pendingCard issuance is blocked until KYC clears
Active:
Sub-StatusDescription
verifiedCard is fully active after activation
reinstatedCard was unfrozen and returned to active use
Suspended:
Sub-StatusDescription
wallet_suspendedUser froze the card voluntarily
lostUser reported the card as lost
stolenUser reported the card as stolen
fraud_suspectedSystem flagged the card for potential fraud
compliance_reviewCard is under compliance review
Closed:
Sub-StatusDescription
expiredCard reached its expiration date
voluntary_closeUser chose to close the card
fraud_confirmedClosed due to confirmed fraud
compliance_closeClosed by compliance action
replacedClosed because a replacement card was issued

Status State Machine

User-Allowed Transitions

Not all transitions can be initiated by the user. The following actions are available through the API:
From StatusAvailable User Actions
active/verifiedFreeze, Report Lost, Report Stolen
active/reinstatedFreeze, Report Lost, Report Stolen
suspended/wallet_suspendedUnfreeze
suspended/lostUnfreeze
pending/activation_requiredActivate
All other transitions (compliance actions, fraud escalation, expiration) are system-initiated and cannot be triggered through these endpoints.

Endpoints

List Cards

GET /v0/cards/
Returns all cards belonging to the authenticated user, each with its current status. Response: 200 OK

Get Card

GET /v0/cards/{card_id}
Returns a single card with its current status. Pass ?include_history=true to include the full status change history.
curl /v0/cards/42?include_history=true \
  -H "x-juno-jwt: <token>"
Response: 200 OK
{
  "id": 42,
  "last_four": "7890",
  "brand": "visa",
  "exp_month": 3,
  "exp_year": 2027,
  "cardholder_name": "Alex Rivera",
  "card_type": "virtual",
  "is_primary": true,
  "linked_wallet_id": 15,
  "created_at": 1700000000000,
  "current_status": {
    "id": 109,
    "card_id": 42,
    "status": "suspended",
    "sub_status": "wallet_suspended",
    "changed_by": "self",
    "created_at": 1700000120000
  },
  "status_history": [
    {
      "id": 109,
      "card_id": 42,
      "status": "suspended",
      "sub_status": "wallet_suspended",
      "changed_by": "self",
      "created_at": 1700000120000
    },
    {
      "id": 108,
      "card_id": 42,
      "status": "active",
      "sub_status": "verified",
      "changed_by": "self",
      "created_at": 1700000060000
    },
    {
      "id": 107,
      "card_id": 42,
      "status": "pending",
      "sub_status": "activation_required",
      "changed_by": "system",
      "created_at": 1700000030000
    }
  ],
  "_links": {
    "self": { "href": "/v0/cards/42" },
    "unfreeze": { "href": "/v0/cards/42/unfreeze", "method": "POST" },
    "user": { "href": "/v0/user/me" },
    "transactions": { "href": "/v0/transactions?card_id=42" },
    "funding": { "href": "/v0/cards/42/funding" },
    "history": { "href": "/v0/cards/42?include_history=true" }
  }
}
The status_history array is ordered newest-first (most recent status change at index 0).

Activate Card

POST /v0/cards/{card_id}/activate
Activates a card that is in pending/activation_required status. Transitions it to active/verified. Response: 200 OK — returns a CardActionResponse with action: "activate".

Freeze Card

POST /v0/cards/{card_id}/freeze
Temporarily suspends an active card. The card moves to suspended/wallet_suspended and can be unfrozen later. Response: 200 OK — returns a CardActionResponse with action: "freeze".

Unfreeze Card

POST /v0/cards/{card_id}/unfreeze
Restores a frozen or lost card to active use. Transitions it to active/reinstated. Response: 200 OK — returns a CardActionResponse with action: "unfreeze".

Report Lost

POST /v0/cards/{card_id}/lost
Reports a card as lost. The card moves to suspended/lost. It can still be unfrozen if found. Response: 200 OK — returns a CardActionResponse with action: "lost".

Report Stolen

POST /v0/cards/{card_id}/stolen
Reports a card as stolen. The card moves to suspended/stolen and the system automatically escalates it to suspended/fraud_suspected for investigation.
Reporting a card as stolen triggers an automatic fraud escalation. Unlike freeze or lost, the resulting fraud_suspected status cannot be reversed by the user — only system or compliance actions can reinstate or close the card.
Response: 200 OK — returns a CardActionResponse with action: "stolen". The current_status reflects the escalated fraud_suspected sub-status.

Action Response Shape

All action endpoints (freeze, unfreeze, lost, stolen, activate) return the same shape:
FieldTypeDescription
idintegerCard ID
last_fourstringLast four digits of the card number
brandstringCard brand (e.g. visa, mastercard)
card_typestringOne of: virtual, physical, metal
is_primarybooleanWhether this is the user’s primary card
created_atintegerCard creation timestamp (epoch ms)
previous_statusobjectStatus before the action
current_statusobjectStatus after the action
actionstringThe action that was performed
_linksobjectAvailable actions based on the new status

Card Funding

Each card has a funding configuration that determines where the card draws funds from when processing transactions.

Get Funding Configuration

GET /v0/cards/{card_id}/funding
curl /v0/cards/42/funding \
  -H "x-juno-jwt: <token>"
Response: 200 OK
{
  "card_id": 42,
  "source_type": null,
  "wallet_id": 15,
  "fiat_account_id": null,
  "configured": true,
  "_links": {
    "self": { "href": "/v0/cards/42/funding" },
    "card": { "href": "/v0/cards/42" },
    "user": { "href": "/v0/user/me" },
    "card-transactions": { "href": "/v0/transactions?card_id=42" },
    "update": { "href": "/v0/cards/42/funding", "method": "PUT" },
    "wallet": { "href": "/v0/wallets/15" },
    "wallet-transactions": { "href": "/v0/transactions?wallet_id=15" },
    "wallet-assets": { "href": "/v0/wallets/15/assets" }
  }
}
FieldTypeDescription
card_idintegerThe card this funding configuration belongs to
source_typestring or nullFunding source type: "wallet" or "fiat"
wallet_idinteger or nullID of the linked wallet (if source is a wallet)
fiat_account_idstring or nullID of the linked bank account (if source is fiat)
configuredbooleanWhether a funding source has been set up

Update Funding Configuration

PUT /v0/cards/{card_id}/funding
curl -X PUT /v0/cards/42/funding \
  -H "x-juno-jwt: <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "source_type": "wallet",
    "wallet_id": 15
  }'
Request body:
FieldTypeRequiredDescription
source_typestringYes"wallet" or "fiat"
wallet_idintegerNoRequired when source_type is "wallet"
fiat_account_idstringNoRequired when source_type is "fiat"
Response: 200 OK

Integration Patterns

Building a Card Management UI

  1. Fetch the user’s cards with GET /v0/cards/
  2. For each card, read current_status to determine its state
  3. Use the _links in each card response to discover available actions — active cards will include freeze, lost, and stolen links; frozen cards will include unfreeze
  4. After performing an action, use the returned current_status and _links to update the UI immediately

Hypermedia-Driven Actions

The _links in card responses change based on the card’s current status. Rather than hardcoding which actions are available for each status, read the links from the response:
card = GET /v0/cards/42

if "freeze" in card._links:
    show_freeze_button(card._links.freeze.href)

if "unfreeze" in card._links:
    show_unfreeze_button(card._links.unfreeze.href)

if "activate" in card._links:
    show_activate_button(card._links.activate.href)
This ensures your UI stays in sync with the backend’s transition rules without duplicating the state machine logic.

Status History for Audit

Use ?include_history=true on the get card endpoint to display a timeline of status changes. Each entry includes changed_by to distinguish user-initiated actions from system or compliance events.
status_history[].changed_by values:
  "self"       — user initiated the action
  "admin"      — platform administrator
  "ops"        — operations team
  "system"     — automated process
  "compliance" — compliance team

Error Handling

All error responses follow RFC 7807 Problem Details format:
{
  "type": "https://api.sumvin.com/errors/crd-404-001",
  "title": "Card Not Found",
  "status": 404,
  "detail": "Card 999 not found",
  "instance": "/v0/cards/999",
  "error_code": "CRD-404-001"
}
StatusError CodeMeaning
400 Bad RequestCRD-400-001Invalid status transition — the card cannot move from its current status to the requested one
401 UnauthorizedMissing or invalid authentication token
403 ForbiddenCRD-403-001Card does not belong to the authenticated user
403 ForbiddenCRD-403-002Action is not allowed for the current card status (e.g. user trying a system-only transition)
404 Not FoundCRD-404-001Card with the given ID does not exist
404 Not FoundCRD-404-002No status record found for the card

Reference Tables

Card Fields

FieldTypeDescription
idintegerUnique card identifier
last_fourstringLast four digits of the card number
brandstringCard brand: visa, mastercard, amex, discover, platform, unknown
exp_monthintegerExpiration month (1-12)
exp_yearintegerExpiration year (4-digit)
cardholder_namestring or nullName printed on the card
card_typestringOne of: virtual, physical, metal
is_primarybooleanWhether this is the user’s primary card
linked_wallet_idinteger or nullID of the linked funding wallet
created_atintegerCreation timestamp (epoch ms)

Status Fields

FieldTypeDescription
idintegerStatus record ID
card_idintegerCard this status belongs to
statusstringPrimary status: pending, active, suspended, closed
sub_statusstring or nullDetailed sub-status within the primary status
changed_bystringWho initiated the change: self, admin, ops, system, compliance
created_atintegerWhen the status change occurred (epoch ms)

Card Brands

ValueDescription
visaVisa
mastercardMastercard
amexAmerican Express
discoverDiscover
platformPlatform-branded card
unknownBrand not determined

Next Steps