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

# Cards

> Issue and manage cards for your cardholders

Cards are issued to cardholders from a card program. Each card has a lifecycle - from issuance through activation to eventual cancellation.

## Prerequisites

Before issuing a card, the cardholder must have KYC status `approved`. See the [cardholders guide](/partner/cardholders) for KYC details.

## Issue a card

Select a card program and issue a card to a cardholder:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.contro.me/v1/partner/cards \
    -H "x-contro-api-key: $CONTRO_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "cardholderId": "ch_abc123",
      "programId": "prog_xyz",
      "idempotencyKey": "issue_card_123"
    }'
  ```

  ```typescript SDK (Node) theme={null}
  const card = await contro.cards.create({
    cardholderId: "ch_abc123",
    programId: "prog_xyz",
    idempotencyKey: "issue_card_123",
  });
  ```

  ```python SDK (Python) theme={null}
  card = contro.cards.create(
      cardholder_id="ch_abc123",
      program_id="prog_xyz",
      idempotency_key="issue_card_123",
  )
  ```

  ```typescript TypeScript (fetch) theme={null}
  const card = await fetch(`${BASE_URL}/partner/cards`, {
    method: "POST",
    headers,
    body: JSON.stringify({
      cardholderId: "ch_abc123",
      programId: "prog_xyz",
      idempotencyKey: "issue_card_123",
    }),
  }).then((r) => r.json());
  ```
</CodeGroup>

### Request fields

| Field | Type | Required | Description |
| - | - | - | - |
| `cardholderId` | string | Yes | Cardholder to issue the card to. Example: `"ch_abc123"` |
| `programId` | string | Yes | Card program to use. Example: `"prog_xyz"` |
| `idempotencyKey` | string | No | Unique key to prevent duplicate card issuance. Example: `"issue_card_user42_2026"` |

### Response

```json theme={null}
{
  "id": "card_def456",
  "status": "created",
  "cardholderId": "ch_abc123",
  "programId": "prog_xyz"
}
```

### Asynchronous issuance

Some card programs provision cards asynchronously. On those programs the response returns `"status": "pending"` instead of `"created"`. The card is not usable yet — provisioning typically completes within a few minutes:

1. Issue the card and store the returned `id`.
2. When provisioning completes, the card transitions to `active` and a [`card.issued`](/partner/webhooks/card-issued) webhook fires.
3. Prefer reacting to the webhook; you can also poll `GET /partner/cards/{id}` until `status` is `active`.

The sandbox reproduces this lifecycle: sandbox-issued cards return `pending` and flip to `active` (with the same `card.issued` webhook) after a short delay, so an integration built against sandbox handles the live timing unchanged.

## Card lifecycle

Cards progress through the following states:

| Action | Endpoint | Effect |
| - | - | - |
| **Activate** | `POST /partner/cards/{id}/activate` | Card becomes usable for transactions |
| **Freeze** | `POST /partner/cards/{id}/freeze` | Temporarily blocks all transactions |
| **Unfreeze** | `POST /partner/cards/{id}/unfreeze` | Re-enables transactions on a frozen card |
| **Cancel** | `POST /partner/cards/{id}/cancel` | Permanently deactivates the card |

<Warning>
  Cancellation is irreversible. A cancelled card cannot be reactivated - issue a new card instead.
</Warning>

### Example: activate a card

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.contro.me/v1/partner/cards/card_def456/activate \
    -H "x-contro-api-key: $CONTRO_API_KEY"
  ```

  ```typescript SDK (Node) theme={null}
  await contro.cards.activate("card_def456");
  ```

  ```python SDK (Python) theme={null}
  contro.cards.activate("card_def456")
  ```
</CodeGroup>

### Example: freeze a card

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.contro.me/v1/partner/cards/card_def456/freeze \
    -H "x-contro-api-key: $CONTRO_API_KEY"
  ```

  ```typescript SDK (Node) theme={null}
  await contro.cards.freeze("card_def456");
  ```

  ```python SDK (Python) theme={null}
  contro.cards.freeze("card_def456")
  ```
</CodeGroup>

## Spend control

Set per-transaction-type velocity caps on a card. Each cap is optional; omit a key to leave that bucket uncapped. The endpoint accepts both the new `spendControl` body and the legacy `{ limit }` body (deprecated, see below).

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PATCH https://api.contro.me/v1/partner/cards/card_def456/limits \
    -H "x-contro-api-key: $CONTRO_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "spendControl": {
        "sales": { "perTransaction": 200, "daily": 1000, "allTime": 50000 },
        "cash": { "allTime": 0 }
      }
    }'
  ```

  ```typescript SDK (Node) theme={null}
  await contro.cards.updateLimits("card_def456", {
    spendControl: {
      sales: { perTransaction: 200, daily: 1000, allTime: 50000 },
      cash: { allTime: 0 },
    },
  });
  ```
</CodeGroup>

See the [Spend control guide](/partner/spend-control) for the full cap matrix, provider capability differences, and common patterns.

<Note>
  The legacy `{ "limit": 5000 }` body is still accepted and is mapped to `{ "spendControl": { "sales": { "allTime": 5000 }, "cash": { "allTime": 5000 } } }`. It will be removed in a future release.
</Note>

## List cards

Retrieve all cards for your partner account:

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.contro.me/v1/partner/cards?limit=20" \
    -H "x-contro-api-key: $CONTRO_API_KEY"
  ```

  ```typescript SDK (Node) theme={null}
  const cards = await contro.cards.list({ limit: 20 });
  ```

  ```python SDK (Python) theme={null}
  cards = contro.cards.list(limit=20)
  ```
</CodeGroup>

### Query parameters

| Parameter | Type | Description |
| - | - | - |
| `page` | integer | Page number (default `1`). |
| `limit` | integer | Items per page (1–100, default `20`). |
| `status` | string | Filter by card status. One of `created`, `active`, `frozen`, `cancelled`. |

## Get a card

Retrieve a single card by ID:

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.contro.me/v1/partner/cards/{id} \
    -H "x-contro-api-key: $CONTRO_API_KEY"
  ```

  ```typescript SDK (Node) theme={null}
  const card = await contro.cards.retrieve("card_def456");
  ```

  ```python SDK (Python) theme={null}
  card = contro.cards.retrieve("card_def456")
  ```
</CodeGroup>

### Card fields

| Field | Type | Description |
| - | - | - |
| `id` | string | Card ID. Example: `"card_def456"` |
| `type` | string | Card type. One of `virtual`, `physical` |
| `brand` | string | Card network brand. One of `Visa`, `Mastercard` |
| `status` | string | Current card status. One of `created`, `active`, `frozen`, `cancelled` |
| `nameOnCard` | string \| null | Name embossed on the card, or `null` for virtual cards. Example: `"JANE DOE"` |
| `limit` | number \| null | **Deprecated.** Mirrors `spendControl.sales.allTime`. Use `spendControl` for per-transaction-type caps. Example: `5000` |
| `spendControl` | object \| null | Per-transaction-type velocity caps (`sales`, `cash`) and accrued spend (`spent`). See [Spend control](/partner/spend-control). |
| `last4` | string \| null | Last 4 digits of the card number. Example: `"0000"` |
| `programId` | string \| null | Card program this card was issued from. Example: `"prog_xyz"` |
| `createdAt` | string | ISO 8601 creation timestamp. Example: `"2026-03-20T14:30:00Z"` |

## Transactions

Retrieve transaction history for a card:

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.contro.me/v1/partner/cards/{id}/transactions?limit=20" \
    -H "x-contro-api-key: $CONTRO_API_KEY"
  ```

  ```typescript SDK (Node) theme={null}
  const txns = await contro.cards.listTransactions("card_def456", { limit: 20 });
  ```

  ```python SDK (Python) theme={null}
  txns = contro.cards.list_transactions("card_def456", limit=20)
  ```
</CodeGroup>

### Transaction fields

| Field | Type | Description |
| - | - | - |
| `id` | string | Transaction ID. Example: `"tx_abc123"` |
| `type` | string | Transaction type. One of `purchase`, `refund`, `withdrawal`, `fee` |
| `maskedCardNo` | string \| null | Last 4 digits of the card number used. Example: `"0000"` |
| `direction` | string \| null | Transaction direction. `C`: credit, `D`: debit |
| `amount` | number \| null | Transaction amount in the card's currency unit |
| `currency` | string \| null | ISO 4217 currency code. Example: `"USD"` |
| `currencyPrecision` | number \| null | Decimal precision of the transaction currency |
| `billingAmount` | number \| null | Billing amount in the billing currency |
| `billingCurrencyCode` | string \| null | ISO 4217 billing currency code |
| `billingCurrencyPrecision` | number \| null | Decimal precision of the billing currency |
| `billingTimestamp` | string \| null | ISO 8601 billing/clearing timestamp |
| `status` | string | Transaction status. One of `pending`, `completed`, `declined`, `reversed` |
| `merchant` | string \| null | Merchant name |
| `timestamp` | string | ISO 8601 transaction timestamp |

## Card programs

Card programs define the card `type` and `brand` available to your integration. Your Contro admin assigns specific card programs to your partner account — only assigned programs can be used to issue cards.

### List available card programs

Retrieve the card programs assigned to your account:

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.contro.me/v1/partner/card-programs?limit=20" \
    -H "x-contro-api-key: $CONTRO_API_KEY"
  ```

  ```typescript SDK (Node) theme={null}
  const programs = await contro.cardPrograms.list({ limit: 20 });
  ```

  ```python SDK (Python) theme={null}
  programs = contro.card_programs.list(limit=20)
  ```
</CodeGroup>

### Card program fields

| Field | Type | Description |
| - | - | - |
| `id` | string | Card program ID. Example: `"prog_xyz"` |
| `name` | string | Program name. Example: `"US Virtual Visa"` |
| `type` | string | Card type. One of `virtual`, `physical` |
| `brand` | string | Card network brand. One of `Visa`, `Mastercard` |
| `active` | boolean | Whether the program is currently active |

<Note>
  Issuing a card with a program not assigned to your account will return a `403` error. Contact your Contro admin to request access to additional card programs.
</Note>
