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

# Physical cards

> The four-step chain that upgrades an ACTIVE virtual card to a physical card under the same number: the virtual-to-physical application (cardLayoutCode, embossing names, address snapshot) → shipping-info tracking → activate (idempotent) → set-pin (AES-GCM encrypted upload).

## 📄 Guide

A physical card comes from an activated (ACTIVE) virtual card upgraded in place through "virtual to physical". The PAN, expiry, CVV2 and `cardId` all stay the same throughout the upgrade — queries return the original virtual card's details before activation and take effect as the physical card after it — so you never need to replace the card or migrate any targets after upgrading. For the card-state background see [State machines and freezing](../basic-concepts/states-and-freezing).

The four-step chain:

<Frame>
  <img className="block dark:hidden" src="https://mintcdn.com/dcs-0bf7a937/oUoeUKtwWWK2o-Li/imgs/en/diagrams/corp-physical-flow-light.svg?fit=max&auto=format&n=oUoeUKtwWWK2o-Li&q=85&s=94eabff1dc4f69c371d5695d98b5dd17" alt="Virtual-to-physical card path" width="665" height="326" data-path="imgs/en/diagrams/corp-physical-flow-light.svg" />

  <img className="hidden dark:block" src="https://mintcdn.com/dcs-0bf7a937/oUoeUKtwWWK2o-Li/imgs/en/diagrams/corp-physical-flow-dark.svg?fit=max&auto=format&n=oUoeUKtwWWK2o-Li&q=85&s=eaa5b069ed259db893a20c5ffad2e0a0" alt="Virtual-to-physical card path" width="665" height="326" data-path="imgs/en/diagrams/corp-physical-flow-dark.svg" />
</Frame>

<Note>
  The shipping address hangs off the employee's record; it is read when the virtual-to-physical application is accepted and **locked as a snapshot** — later address changes do not affect cards already in production.
</Note>

## ① Upgrade application: virtual to physical

```http theme={null}
POST /open-api-corp/card/v1/virtual-to-physical
```

Upgrades an ACTIVE virtual card to a physical card under the same number.

### Request parameters

| Field            | Type   | Required | Description                                                                                                                                                                                            |
| ---------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `cardApplyRef`   | String | Yes      | ≤64; your unique application identifier (the idempotency key); character set `^[A-Za-z0-9_-]+$`                                                                                                        |
| `organizationId` | String | Yes      | ≤20; the card's owning company                                                                                                                                                                         |
| `cardId`         | String | Yes      | ≤20; the virtual card being upgraded; must belong to your partnership / company, currently be a virtual card and be ACTIVE; the holder / custodian and the shipping address are derived from this card |
| `cardLayoutCode` | String | Yes      | The card-face code; must be a configured face (values are issued by DCS per partner configuration — not a fixed enum)                                                                                  |
| `embossingName`  | String | Yes      | ≤26; the first embossed line on the card face                                                                                                                                                          |
| `embossingName2` | String | No       | ≤26; the second embossed line (optional)                                                                                                                                                               |

### Response data

| Field         | Type   | Description               |
| ------------- | ------ | ------------------------- |
| `cardApplyId` | String | The upgrade acceptance id |

### Request and response examples

```json theme={null}
{
  "cardApplyRef": "ext-v2p-0001",
  "organizationId": "e7c9a1f0-3b52-4d8e-9a67-1f2b3c4d5e6f",
  "cardId": "5185740066240790530",
  "cardLayoutCode": "LAYOUT_CLASSIC_BLACK",
  "embossingName": "DOE JOHN",
  "embossingName2": "EXAMPLE HK LIMITED"
}
```

```json theme={null}
{
  "code": "SYS_SUCCESS",
  "message": null,
  "data": {
    "cardApplyId": "5136744097353943557"
  }
}
```

### Error codes

| Error code                  | Description                                                                   |
| --------------------------- | ----------------------------------------------------------------------------- |
| `CARD_INVALID`              | The card does not exist / does not belong to your partnership / is not ACTIVE |
| `CARD_NOT_VIRTUAL`          | The card is already physical and cannot be converted again                    |
| `CARD_CONVERT_IN_PROGRESS`  | The same card already has an in-flight (unfinished) upgrade application       |
| `APPLY_DUPLICATE`           | The same `cardApplyRef` has already been accepted (idempotency)               |
| `SHIPPING_ADDRESS_REQUIRED` | The holder / custodian has no shipping address on file                        |
| `COUNTRY_SANCTIONED`        | The destination country is on a sanctions list                                |
| `LIMIT_PHYSICAL_EXCEEDED`   | Over the physical-card cap (company-level or holder-level)                    |

Related webhooks: `CARD_SHIPPED` (dispatched) / `CARD_ACTIVATED` (activated).

## ② Tracking production and shipping

```http theme={null}
GET /open-api-corp/card/v1/shipping-info
```

Queries the physical card's production and shipping progress plus the tracking number.

### Request parameters

| Field            | Type   | Required | Description                    |
| ---------------- | ------ | -------- | ------------------------------ |
| `organizationId` | String | Yes      | ≤20; the card's owning company |
| `cardId`         | String | Yes      | ≤20; the card id               |

### Response data

| Field                 | Type   | Description                                 |
| --------------------- | ------ | ------------------------------------------- |
| `cardId`              | String | The card id                                 |
| `trackingNumber`      | String | The tracking number (empty before dispatch) |
| `trackingCompanyName` | String | The carrier                                 |

### Response examples (shipped / not shipped)

```json theme={null}
// Scenario 1: shipped
{
  "code": "SYS_SUCCESS",
  "message": null,
  "data": {
    "cardId": "5185740066240790530",
    "trackingNumber": "SF1234567890123",
    "trackingCompanyName": "SF Express"
  }
}

// Scenario 2: not yet shipped
{
  "code": "SYS_SUCCESS",
  "message": null,
  "data": {
    "cardId": "5185740066240790530",
    "trackingNumber": null,
    "trackingCompanyName": null
  }
}
```

Error code: `CARD_INVALID` — the card does not exist / does not belong to your partnership.

## ③ Activating the physical card

```http theme={null}
POST /open-api-corp/card/v1/activate
```

Activates the physical card; the prerequisite is that the card **has shipped**. **The endpoint is idempotent** — repeated calls return the same success.

### Request parameters and response

Request parameters: `cardId` (String, required, ≤32; the card id). Card-scoped endpoints locate by `cardId` alone and never carry `organizationId`. This is an action endpoint: `data` is `null`.

```json theme={null}
{
  "organizationId": "e7c9a1f0-3b52-4d8e-9a67-1f2b3c4d5e6f",
  "cardId": "5185740066240790530"
}
```

```json theme={null}
{
  "code": "SYS_SUCCESS",
  "message": null,
  "data": {
    "cardId": "5185740066240790530"
  }
}
```

### Error codes

| Error code         | Description                                                   |
| ------------------ | ------------------------------------------------------------- |
| `CARD_INVALID`     | The card does not exist / does not belong to your partnership |
| `CARD_NOT_SHIPPED` | The card has not shipped yet and cannot be activated          |

Related webhook: `CARD_ACTIVATED`.

## ④ Setting the PIN

```http theme={null}
POST /open-api-corp/card/v1/set-pin
```

Sets the physical card's PIN; the prerequisite is that the physical card **has been activated**.

<Note>
  The PIN and the identity-check fields are AES-GCM encrypted with your SK before upload — **the same crypto contract as `retrieve-secure-card`, in the opposite direction** (for the contract see [Retrieving secure card details](./secure-card-details): AES/GCM/NoPadding, 128-bit tag, keyed with the SK).
</Note>

### Request parameters

| Field                 | Type   | Required | Description                                                                                                                    |
| --------------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `organizationId`      | String | Yes      | ≤20; the card's owning company                                                                                                 |
| `cardId`              | String | Yes      | ≤20; the card id                                                                                                               |
| `encryptedPin`        | String | Yes      | The encrypted PIN (AES-GCM under the partner key; the clear PIN must be 4 digits, no sequential runs, no all-identical digits) |
| `encryptedCvv2`       | String | Yes      | The encrypted CVV2 (identity check)                                                                                            |
| `encryptedExpireDate` | String | Yes      | The encrypted expiry (identity check; plaintext format mm/yy)                                                                  |
| `encryptedPanLast4`   | String | Yes      | The encrypted last four digits of the PAN (identity check)                                                                     |
| `iv`                  | String | Yes      | The per-call random IV (Base64); the 4 ciphertext fields share the same `iv`                                                   |

### Request and response examples

```json theme={null}
{
  "organizationId": "e7c9a1f0-3b52-4d8e-9a67-1f2b3c4d5e6f",
  "cardId": "5185740066240790530",
  "encryptedPin": "UGluTW9ja0NpcGhlclRleHQrVGFnMTZC",
  "encryptedCvv2": "Q3Z2Mk1vY2tDaXBoZXJUZXh0K1RhZzE2Qg==",
  "encryptedExpireDate": "RXhwTW9ja0NpcGhlclRleHQrVGFnMTZC",
  "encryptedPanLast4": "TGFzdDRNb2NrQ2lwaGVyVGV4dCtUYWcxNkI=",
  "iv": "MTIzNDU2Nzg5MGFi"
}
```

The response data carries `cardId` and `success` (Boolean, whether the PIN was set):

```json theme={null}
{
  "code": "SYS_SUCCESS",
  "message": null,
  "data": {
    "cardId": "5185740066240790530",
    "success": true
  }
}
```

### Error codes

| Error code               | Description                                                                   |
| ------------------------ | ----------------------------------------------------------------------------- |
| `CARD_INVALID`           | The card does not exist / does not belong to your partnership                 |
| `PIN_CARD_NOT_ACTIVATED` | The card is not activated; the PIN cannot be set                              |
| `PIN_CARD_STATE_INVALID` | The card state does not allow setting a PIN (not ACTIVE / frozen by the user) |
| `PIN_RULE_VIOLATION`     | The PIN is not 4 digits / is a sequential run / is all-identical digits       |

## Next steps

* No upgradable virtual card yet? Issue one first: [Applying for a virtual card](./applying-virtual-cards)
* Card states and the freeze capability domains: [State machines and freezing](../basic-concepts/states-and-freezing)
