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

# Cards

> Virtual prepaid cards issued under a product. The product defines all card settings — scheme, currency, limits, 3DS. Cards belong to a cardholder but are not tied to any program directly.

# Cards

A **card** is a virtual prepaid payment instrument. It carries a PAN, CVV, expiry date, and a spendable balance. Cards are issued against a **product** — the product is the template that determines the card's scheme (Visa/Mastercard), currency, spending limits, 3DS, and tokenization support.

## Prerequisites for Issuance

Two conditions must be met before a card can be issued:

1. **Cardholder `kycStatus` must be `APPROVED`** — attempting to issue to an unapproved cardholder returns `422 CARDHOLDER_KYC_NOT_APPROVED`
2. **The product's program must be `ACTIVE`** — a paused or closed program blocks card creation under that product

Both conditions can be checked upfront. Cards are issued instantly once met, in both `SANDBOX` and `LIVE`.

## Issuing a Card

You need a `cardholderId` and a `productId`. The product determines everything else — program, scheme, currency, and limits — so there is no need to specify a `programId`.

```bash theme={null}
curl -X POST https://api.fyatu.com/api/v3.20/cards \
  -H "Authorization: Bearer $FYATU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "cardholderId": "chl_01HXYZ1234ABCDEF5678",
    "productId":    "prd_01HXYZABC123"
  }'
```

The card starts with **zero balance**. Use `POST /cards/{id}/fund` to load it before the cardholder can spend.

Discover available `productId` values via `GET /programs/{programId}/products`.

## Card Lifecycle

```
POST /cards
      │
      ▼
  status: ACTIVE  ◄─── zero balance, ready to fund
      │
      ├── POST /cards/{id}/fund  ─────► balance increases
      ├── POST /cards/{id}/unload ────► balance decreases
      │
      ├── POST /cards/{id}/freeze ───► status: FROZEN
      │       │
      │       └── POST /cards/{id}/unfreeze ─► status: ACTIVE
      │
      └── POST /cards/{id}/terminate ─► status: TERMINATED (irreversible)
                                           remaining balance refunded to program ledger
```

## Card Statuses

| Status       | Can transact | Can fund | Can unload | Description                        |
| ------------ | ------------ | -------- | ---------- | ---------------------------------- |
| `ACTIVE`     | Yes          | Yes      | Yes        | Normal operating state             |
| `FROZEN`     | No           | Yes      | Yes        | Purchases blocked — balance intact |
| `TERMINATED` | No           | No       | No         | Permanently closed                 |

## Balance Flow

All card operations move money between the **program ledger** and the **card balance**:

| Operation                    | Program ledger         | Card balance             |
| ---------------------------- | ---------------------- | ------------------------ |
| `POST /cards/{id}/fund`      | Debited                | Credited                 |
| `POST /cards/{id}/unload`    | Credited               | Debited                  |
| `POST /cards/{id}/terminate` | Credited (full refund) | → 0                      |
| Authorization (APPROVED)     | No change              | Held (reduces available) |
| Clearing                     | No change              | Decreases                |
| Reversal                     | No change              | Increases                |

**You never lose money when terminating a card.** The full remaining balance returns to the program ledger automatically.

## Fund a Card

```bash theme={null}
curl -X POST https://api.fyatu.com/api/v3.20/cards/crd_01HXYZ.../fund \
  -H "Authorization: Bearer $FYATU_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: fund-crd01HXYZ-order-001" \
  -d '{ "amount": 100.00, "note": "Monthly allowance" }'
```

* `amount` is in dollars (`100.00` = \$100)
* Always use `Idempotency-Key` — if your request times out and you retry, the card won't be double-funded
* `CARD_FUNDED` webhook fires on success; `CARD_FUND_FAILED` on failure
* **Funding a frozen card is allowed** — balance is added; spending re-enables after unfreezing

## Unload a Card

```bash theme={null}
curl -X POST https://api.fyatu.com/api/v3.20/cards/crd_01HXYZ.../unload \
  -H "Authorization: Bearer $FYATU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 50.00, "note": "Partial reclaim" }'
```

* Cannot unload more than the card's current available balance → `422 INSUFFICIENT_CARD_BALANCE`
* `CARD_UNLOADED` webhook fires on success; `CARD_UNLOAD_FAILED` on failure

## Freeze and Unfreeze

Temporarily block a card from making purchases without touching its balance:

```bash theme={null}
POST /cards/{id}/freeze    # → status: FROZEN
POST /cards/{id}/unfreeze  # → status: ACTIVE
```

* A frozen card rejects all authorization attempts at the card network level
* Frozen cards can still be funded and unloaded
* `409 CARD_ALREADY_FROZEN` / `409 CARD_NOT_FROZEN` guard against double-transitions

**Common scenario:** cardholder reports a potentially lost card. Freeze it immediately while investigating. Unfreeze if found; terminate and replace if not.

## Terminate a Card

Termination is permanent and irreversible. The `"confirm"` field prevents accidental calls:

```bash theme={null}
curl -X POST https://api.fyatu.com/api/v3.20/cards/crd_01HXYZ.../terminate \
  -H "Authorization: Bearer $FYATU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "confirm": "TERMINATE_CARD" }'
```

Omitting `"confirm": "TERMINATE_CARD"` returns `400 CONFIRMATION_REQUIRED`.

**What happens:**

1. All pending authorizations are reversed
2. Remaining balance is returned to the program ledger
3. Card status becomes `TERMINATED` permanently
4. `CARD_TERMINATED` webhook fires

**If the card has pending transactions:** the API returns `409 CARD_HAS_PENDING_TRANSACTIONS`. Authorizations typically clear within 24–72 hours. Retry after they settle.

## Replace a Card

Issue a replacement for a compromised or lost card:

```bash theme={null}
POST /cards/{id}/replace
```

The original card is terminated and a new card is issued under the same product. The remaining balance transfers automatically to the new card — it does not pass through the program ledger. `CARD_TERMINATED` fires for the old card and `CARD_ISSUED` for the new one.

## Masking and Sensitive Data

The CaaS API never returns full card numbers or CVV. All card data is masked:

| Field                        | Returned value         |
| ---------------------------- | ---------------------- |
| `maskedPan`                  | `**** **** **** 4242`  |
| `last4`                      | `4242`                 |
| `expiryMonth` / `expiryYear` | `5` / `2029`           |
| Full PAN                     | Never returned via API |
| CVV                          | Never returned via API |

Full card credentials are displayed to the cardholder through your in-app secure card widget or the CaaS-provided display component. Contact your account manager for the secure card-detail integration guide.

## Just-In-Time (JIT) Cards

Some products support **JIT funding** — cards issued with no pre-loaded balance. When the cardholder makes a purchase, Fyatu checks your program ledger balance in real time and approves or declines the authorization automatically.

JIT is controlled by the product (`hasJIT: true`). If a product has JIT enabled, `amount` is optional at card creation time:

```bash theme={null}
# JIT card — no amount required
curl -X POST https://api.fyatu.com/api/v3.20/cards \
  -H "Authorization: Bearer $FYATU_API_KEY" \
  -d '{ "cardholderId": "chl_...", "productId": "prd_JIT_PRODUCT_ID" }'
```

The `CARD_ISSUED` webhook will include `"isJitfEnabled": true`. Every authorization attempt then fires `CARD_AUTHORIZATION` with the decision.

See the [JIT Cards guide](/v3.20/documentation/concepts/jit-cards) for the full flow, sandbox testing, and program balance management.

## Webhook Events

| Event                | Fired when                                                     |
| -------------------- | -------------------------------------------------------------- |
| `CARD_ISSUED`        | Card successfully issued (`isJitfEnabled: true` for JIT cards) |
| `CARD_AUTHORIZATION` | Authorization approved or declined (JIT and pre-funded)        |
| `CARD_FUNDED`        | Balance added to a card                                        |
| `CARD_FUND_FAILED`   | Fund operation failed                                          |
| `CARD_UNLOADED`      | Balance returned from a card to the program ledger             |
| `CARD_UNLOAD_FAILED` | Unload operation failed                                        |
| `CARD_FROZEN`        | Card frozen                                                    |
| `CARD_UNFROZEN`      | Card unfrozen                                                  |
| `CARD_TERMINATED`    | Card permanently closed                                        |

## Endpoints

* `POST /cards` — [Issue card](/v3.20/api-reference/cards/create)
* `GET /cards` — [List cards](/v3.20/api-reference/cards/list)
* `GET /cards/{id}` — [Get card](/v3.20/api-reference/cards/get)
* `POST /cards/{id}/fund` — [Fund card](/v3.20/api-reference/cards/fund)
* `POST /cards/{id}/unload` — [Unload card](/v3.20/api-reference/cards/unload)
* `POST /cards/{id}/freeze` — [Freeze card](/v3.20/api-reference/cards/freeze)
* `POST /cards/{id}/unfreeze` — [Unfreeze card](/v3.20/api-reference/cards/unfreeze)
* `POST /cards/{id}/terminate` — [Terminate card](/v3.20/api-reference/cards/terminate)
