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

# Set Card PIN

> Let a cardholder set their PIN through Yativo's secure hosted page — no PIN ever touches your server.

Card PINs are set — and changed — through Yativo's **hosted secure page**, powered by the Gnosis Pay Partner Secure Elements (PSE) service. The cardholder types their PIN directly inside a sandboxed iframe. Your backend never receives or processes the PIN value.

<Note>
  **First-time PIN setup and PIN changes use different response shapes.**

  * **First-time setup** (`pin_set: false`): any call to `view-token` returns `422 PIN_NOT_SET`. The response body includes `data.pin_setup_url` — open that URL for PIN setup. You do not need to pass `enabled_views: ["pin"]`; the API auto-selects the PIN-setup view.
  * **PIN change** (`pin_set: true`): call `view-token` with `enabled_views: ["pin"]`. You get `200` with `data.secure_view_url` — open that for PIN change.
</Note>

<Note>
  **`pin_set` is informational, not a hard gate.** Yativo tracks `pin_set` from the `physical.card.pin.changed` webhook — Gnosis Pay does not expose this value via their API. The flag may lag behind reality if the webhook was missed or the cardholder set their PIN through another path. Use it as a UI hint (e.g. show a "Set PIN" prompt) rather than blocking access server-side.
</Note>

***

## How it works

### First-time PIN setup (`pin_set: false`)

<Steps>
  <Step title="Call view-token (any enabled_views)">
    Call `POST /v1/yativo-card/{yativoCardId}/cards/{cardId}/view-token`. You do not need to pass `enabled_views: ["pin"]` — the API detects that the PIN is not set and overrides the view automatically.
  </Step>

  <Step title="You receive 422 PIN_NOT_SET with pin_setup_url">
    The API returns HTTP 422 with `error_code: "PIN_NOT_SET"`. The response body includes `data.pin_setup_url` — a short-lived URL pre-configured for PIN setup.
  </Step>

  <Step title="Open pin_setup_url in an iframe or WebView">
    Pass `data.pin_setup_url` to your frontend. The cardholder enters and confirms their 4-digit PIN directly in the hosted page. The PIN never leaves the iframe.
  </Step>

  <Step title="Gnosis Pay fires a webhook">
    Once saved, Gnosis Pay sends a `physical.card.pin.changed` event. Yativo updates `pin_set: true` on the card record automatically.
  </Step>
</Steps>

### PIN change (`pin_set: true`)

<Steps>
  <Step title="Call view-token with enabled_views: [&#x22;pin&#x22;]">
    Call `POST /v1/yativo-card/{yativoCardId}/cards/{cardId}/view-token` with `enabled_views: ["pin"]`.
  </Step>

  <Step title="You receive 200 with secure_view_url">
    The API returns HTTP 200 with `data.secure_view_url` — open that in an iframe or WebView for the PIN change form.
  </Step>
</Steps>

***

## Step 1 — Request a PIN view token

```
POST /v1/yativo-card/{yativoCardId}/cards/{cardId}/view-token
```

<ParamField path="yativoCardId" type="string" required>
  The Yativo Card account ID (`yativo_card_id`).
</ParamField>

<ParamField path="cardId" type="string" required>
  The card ID to set the PIN for.
</ParamField>

<ParamField body="enabled_views" type="array" required>
  Pass `["pin"]` to open the PIN-setting form. Pass `["data", "pin"]` to show both card details and PIN (only works after PIN is already set).
</ParamField>

<ParamField body="theme" type="object">
  Optional brand customization — `accent_color`, `logo_url`, `background_color`, etc. See [Secure Card Display](/yativo-crypto/cards/secure-display) for all options.
</ParamField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST 'https://crypto-api.yativo.com/api/v1/yativo-card/yativo_card_0xAbCd1234_1769031332068/cards/afeb85fe-02f8-48da-b61e-84ad02704167/view-token' \
    -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
    -H 'Content-Type: application/json' \
    -d '{
      "enabled_views": ["pin"],
      "theme": {
        "accent_color": "#6366f1",
        "logo_url": "https://yourapp.com/logo.png"
      }
    }'
  ```

  ```javascript Node.js theme={null}
  const response = await fetch(
    'https://crypto-api.yativo.com/api/v1/yativo-card/yativo_card_0xAbCd1234_1769031332068/cards/afeb85fe-02f8-48da-b61e-84ad02704167/view-token',
    {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        enabled_views: ['pin'],
        theme: { accent_color: '#6366f1' },
      }),
    }
  );
  const { data } = await response.json();
  // Send data.secure_view_url to your frontend
  ```
</RequestExample>

<ResponseExample>
  ```json 200 Token created theme={null}
  {
    "success": true,
    "data": {
      "secure_view_url": "https://crypto-api.yativo.com/api/v1/yativo-card/view/eyJhbGci...",
      "url": "https://crypto-api.yativo.com/api/v1/yativo-card/view/eyJhbGci...",
      "expires_at": "2026-05-18T12:05:00.000Z",
      "last_four": "4291",
      "card_type": "physical",
      "holder_name": "Jane Doe",
      "pin_set": false,
      "enabled_views": ["pin"],
      "requires_access_code": false
    }
  }
  ```
</ResponseExample>

***

## Step 2 — Embed the hosted PIN page

Pass `secure_view_url` as the `src` of an iframe. No SDK or client-side JavaScript required.

```html theme={null}
<iframe
  src="https://crypto-api.yativo.com/api/v1/yativo-card/view/eyJhbGci..."
  title="Set Card PIN"
  width="420"
  height="520"
  style="border: 0; border-radius: 16px;"
  allow="clipboard-read; clipboard-write"
></iframe>
```

<Tip>
  Recommended dimensions for PIN-only view: `420 × 520px`. For combined card details + PIN view: `420 × 740px`.
</Tip>

***

## Theme customization

Pass a `theme` object in the view-token request to brand the hosted PIN page. All theme fields are optional.

```bash theme={null}
curl -X POST '.../view-token' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "enabled_views": ["pin"],
    "theme": {
      "accent_color": "#6366f1",
      "background_color": "#f5f1ea",
      "logo_url": "https://yourapp.com/logo.png",
      "border_radius": 16,
      "font_family": "Inter, sans-serif"
    }
  }'
```

Theme fields: `accent_color`, `background_color`, `panel_color`, `text_color`, `muted_color`, `border_radius`, `font_family`, `logo_url`. See [Secure Card Display](/yativo-crypto/cards/secure-display) for the full reference.

***

## Checking `pin_set` status

After the PIN is set, the Gnosis Pay `physical.card.pin.changed` webhook fires and Yativo updates `pin_set: true` on the card record. You can read this via:

* **Card issuers** — `GET /v1/card-issuer/customers/{customerId}` → `data.cards[n].pin_set`
* **End users** — `GET /v1/yativo-card/my-account` → `data.account.cards[n].pin_set`
* **View-token response** — `data.pin_set` is returned on every `POST .../view-token` call

The `next_action` field on the issuer customer record will read:

> `"Card is active — customer must set card PIN before in-store (PSE) payments will work"`

until `pin_set` becomes `true`.

<Warning>
  `pin_set` reflects the **last known state from the webhook**. Gnosis Pay does not expose this flag via their partner API, so if the webhook was missed or the PIN was set through another path, the value may be stale. Treat it as a UI hint — not an authoritative server-side gate.
</Warning>

***

## Notes

| Detail                | Value                                                                             |
| --------------------- | --------------------------------------------------------------------------------- |
| PIN input             | Cardholder types directly into the hosted iframe — PIN never reaches your server  |
| PIN format            | 4 digits (`0000`–`9999`)                                                          |
| Initial set & change  | Same `enabled_views: ["pin"]` view handles both                                   |
| Confirmation          | `physical.card.pin.changed` webhook → `pin_set: true` in DB                       |
| `pin_set` reliability | Informational — sourced from webhook only, not Gnosis API                         |
| Issuer theme          | Pass `theme` in the view-token request — applies to the hosted PIN page           |
| Online vs chip PIN    | PSE updates the **online PIN**. The chip PIN syncs automatically on first ATM use |
| Works for             | Virtual and physical cards                                                        |
