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

# Generate PIN Set Token

> Generate a secure token for setting or changing the card PIN through a PCI-compliant hosted interface

## Overview

Generates a time-limited secure token that allows users to set or change their card PIN through a PCI-compliant hosted interface. The hosted page includes built-in validation and confirmation flows to ensure the PIN is set correctly and securely.

<Info>
  **PCI Compliance**

  This endpoint maintains PCI compliance by handling PIN creation entirely within secure hosted environments. Your application never handles or stores PIN values.
</Info>

## Authentication

This endpoint requires authentication via Bearer token:

```bash theme={null}
Authorization: Bearer YOUR_ACCESS_TOKEN
```

## Request

### Headers

<ParamField header="x-client-key" type="string" required>
  Your public API client key
</ParamField>

<ParamField header="x-us-env" type="boolean" default={false}>
  Set to `true` to route requests to the US backend environment
</ParamField>

<ParamField header="Authorization" type="string" required>
  Bearer token for authentication
</ParamField>

### Body

<ParamField body="redirectUrl" type="string">
  URL to redirect to after the user completes or cancels PIN setup

  **Only used when `isEmbedded=false`**

  **Example:** `https://yourapp.com/dashboard`
</ParamField>

<ParamField body="isEmbedded" type="boolean" default={false}>
  Controls the completion/dismissal behavior:

  * `false` - Redirects to `redirectUrl` when user completes or cancels
  * `true` - Fires a `postMessage` event with type `abort` for iframe integration
</ParamField>

<ParamField body="customCss" type="object">
  Customize the visual appearance of the hosted PIN setup page

  <Expandable title="customCss properties">
    <ParamField body="customCss.backgroundColor" type="string" default="#EFEFEF">
      Main background color

      **Example:** `#F9FAFB`, `#E5E7EB`
    </ParamField>

    <ParamField body="customCss.textColor" type="string" default="#000000">
      Text color against main background

      **Important:** Avoid using the same hex as `backgroundColor`

      **Example:** `#000000`, `#111827`
    </ParamField>

    <ParamField body="customCss.backgroundColorPrimary" type="string" default="#000000">
      Primary background color for buttons and input areas

      **Example:** `#000000`, `#1F2937`
    </ParamField>

    <ParamField body="customCss.textColorPrimary" type="string" default="#FFFFFF">
      Text color for buttons and input areas

      **Important:** Avoid using the same hex as `backgroundColorPrimary`

      **Example:** `#FFFFFF`, `#F3F4F6`
    </ParamField>

    <ParamField body="customCss.buttonBorderRadius" type="number" default={8}>
      Button border radius in pixels

      **Example:** `4`, `8`, `12`
    </ParamField>

    <ParamField body="customCss.pinBorderRadius" type="number" default={4}>
      PIN input container border radius in pixels

      **Example:** `2`, `4`, `8`
    </ParamField>

    <ParamField body="customCss.pinBorderColor" type="string">
      Border color for PIN input container

      **Default:** Same as `backgroundColorPrimary`

      **Example:** `#3B82F6`, `#10B981`
    </ParamField>
  </Expandable>
</ParamField>

### Request Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://dev.api.baanx.com/v1/card/set-pin/token \
    -H "x-client-key: YOUR_CLIENT_KEY" \
    -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "redirectUrl": "https://yourapp.com/dashboard",
      "isEmbedded": false,
      "customCss": {
        "backgroundColor": "#EFEFEF",
        "textColor": "#000000",
        "backgroundColorPrimary": "#000000",
        "textColorPrimary": "#FFFFFF",
        "buttonBorderRadius": 8,
        "pinBorderRadius": 4,
        "pinBorderColor": "#FF00FF"
      }
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://dev.api.baanx.com/v1/card/set-pin/token', {
    method: 'POST',
    headers: {
      'x-client-key': 'YOUR_CLIENT_KEY',
      'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      redirectUrl: 'https://yourapp.com/dashboard',
      isEmbedded: false,
      customCss: {
        backgroundColor: '#EFEFEF',
        textColor: '#000000',
        backgroundColorPrimary: '#000000',
        textColorPrimary: '#FFFFFF',
        buttonBorderRadius: 8,
        pinBorderRadius: 4
      }
    })
  });

  const data = await response.json();
  console.log('Hosted page URL:', data.hostedPageUrl);
  ```

  ```python Python theme={null}
  import requests

  url = "https://dev.api.baanx.com/v1/card/set-pin/token"
  headers = {
      "x-client-key": "YOUR_CLIENT_KEY",
      "Authorization": "Bearer YOUR_ACCESS_TOKEN",
      "Content-Type": "application/json"
  }
  payload = {
      "redirectUrl": "https://yourapp.com/dashboard",
      "isEmbedded": False,
      "customCss": {
          "backgroundColor": "#EFEFEF",
          "textColor": "#000000",
          "backgroundColorPrimary": "#000000",
          "textColorPrimary": "#FFFFFF",
          "buttonBorderRadius": 8,
          "pinBorderRadius": 4
      }
  }

  response = requests.post(url, headers=headers, json=payload)
  print(response.json())
  ```

  ```typescript TypeScript theme={null}
  interface SetPinTokenRequest {
    redirectUrl?: string;
    isEmbedded?: boolean;
    customCss?: {
      backgroundColor?: string;
      textColor?: string;
      backgroundColorPrimary?: string;
      textColorPrimary?: string;
      buttonBorderRadius?: number;
      pinBorderRadius?: number;
      pinBorderColor?: string;
    };
  }

  interface SetPinTokenResponse {
    token: string;
    hostedPageUrl: string;
  }

  const generateSetPinToken = async (
    config: SetPinTokenRequest
  ): Promise<SetPinTokenResponse> => {
    const response = await fetch('https://dev.api.baanx.com/v1/card/set-pin/token', {
      method: 'POST',
      headers: {
        'x-client-key': 'YOUR_CLIENT_KEY',
        'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(config)
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    return await response.json();
  };
  ```
</CodeGroup>

## Response

### Success Response

<ResponseField name="token" type="string">
  Secure, time-limited token (UUID format)

  **Lifetime:** \~10 minutes

  **Usage:** Single-use token that becomes invalid after access
</ResponseField>

<ResponseField name="hostedPageUrl" type="string">
  Full URL to the hosted PIN setup page

  **Usage:** Redirect users or embed in iframe

  **Format:** `<HOST>/pin-direct/set?token={token}`
</ResponseField>

<ResponseExample>
  ```json 200 - Success theme={null}
  {
    "token": "100a99cf-f4d3-4fa1-9be9-2e9828b20ebb",
    "hostedPageUrl": "https://cards.baanx.com/pin-direct/set?token=100a99cf-f4d3-4fa1-9be9-2e9828b20ebb"
  }
  ```
</ResponseExample>

## Error Responses

<ResponseExample>
  ```json 401 - Unauthorized theme={null}
  {
    "message": "Not authenticated"
  }
  ```

  ```json 403 - Forbidden theme={null}
  {
    "message": "Not authorized"
  }
  ```

  ```json 404 - Card Not Found theme={null}
  {
    "message": "Card not found"
  }
  ```

  ```json 498 - Invalid Client Key theme={null}
  {
    "message": "Invalid client key"
  }
  ```

  ```json 499 - Missing Client Key theme={null}
  {
    "message": "Missing client key"
  }
  ```

  ```json 500 - Internal Server Error theme={null}
  {
    "message": "Internal server error"
  }
  ```
</ResponseExample>

## Hosted Page Features

The hosted PIN setup page includes:

<CardGroup cols={2}>
  <Card title="PIN Entry" icon="keyboard">
    Secure input fields for entering 4-digit PIN
  </Card>

  <Card title="Confirmation" icon="shield-check">
    Two-step entry to prevent typos
  </Card>

  <Card title="Validation" icon="circle-check">
    Real-time validation of PIN format and strength
  </Card>

  <Card title="Masked Input" icon="eye-slash">
    PIN digits are masked for privacy during entry
  </Card>
</CardGroup>

## Integration Methods

<Tabs>
  <Tab title="Full Page Redirect">
    Redirect the user to set their PIN on a dedicated page.

    ```javascript theme={null}
    const { hostedPageUrl } = await generateSetPinToken({
      redirectUrl: 'https://yourapp.com/success',
      isEmbedded: false
    });

    window.location.href = hostedPageUrl;
    ```

    **User Flow:**

    1. User redirected to hosted page
    2. Enters new PIN twice for confirmation
    3. Submits PIN
    4. Redirected to `redirectUrl` on success
  </Tab>

  <Tab title="Iframe Embedding">
    Embed the PIN setup within your application.

    ```javascript theme={null}
    const { hostedPageUrl } = await generateSetPinToken({
      isEmbedded: true
    });

    const iframe = document.createElement('iframe');
    iframe.src = hostedPageUrl;
    iframe.style.width = '100%';
    iframe.style.height = '500px';

    window.addEventListener('message', (event) => {
      if (event.data.type === 'abort') {
        iframe.remove();
      }
    });

    document.getElementById('pin-container').appendChild(iframe);
    ```

    **User Flow:**

    1. Iframe loads within your app
    2. User enters and confirms PIN
    3. `postMessage` event fires on completion or cancellation
    4. Your app handles the event appropriately
  </Tab>
</Tabs>

## Use Case Examples

### First-Time PIN Setup

```typescript theme={null}
async function setupCardPIN() {
  try {
    const { hostedPageUrl } = await generateSetPinToken({
      redirectUrl: `${window.location.origin}/card-activated`,
      isEmbedded: false
    });

    showInfo('You will now set your card PIN');
    window.location.href = hostedPageUrl;
  } catch (error) {
    console.error('Failed to initiate PIN setup:', error);
    showError('Unable to set PIN. Please try again.');
  }
}
```

### Change Existing PIN

```typescript theme={null}
async function changePIN() {
  const confirmed = await showConfirmDialog({
    title: 'Change PIN?',
    message: 'You will be able to set a new 4-digit PIN for your card.',
    confirmText: 'Change PIN',
    cancelText: 'Cancel'
  });

  if (confirmed) {
    try {
      const { hostedPageUrl } = await generateSetPinToken({
        redirectUrl: window.location.href,
        isEmbedded: false
      });

      window.location.href = hostedPageUrl;
    } catch (error) {
      console.error('Failed to change PIN:', error);
      showError('Unable to change PIN. Please try again.');
    }
  }
}
```

### Modal PIN Setup

```typescript theme={null}
function PINSetup() {
  const [showModal, setShowModal] = useState(false);
  const [iframeUrl, setIframeUrl] = useState('');

  const handleSetPIN = async () => {
    try {
      const { hostedPageUrl } = await generateSetPinToken({
        isEmbedded: true,
        customCss: {
          backgroundColor: '#F9FAFB',
          textColor: '#111827',
          backgroundColorPrimary: '#3B82F6',
          textColorPrimary: '#FFFFFF',
          buttonBorderRadius: 8,
          pinBorderRadius: 4
        }
      });

      setIframeUrl(hostedPageUrl);
      setShowModal(true);
    } catch (error) {
      console.error('Failed to load PIN setup:', error);
    }
  };

  useEffect(() => {
    const handleMessage = (event: MessageEvent) => {
      if (event.data.type === 'abort') {
        setShowModal(false);
        setIframeUrl('');
      }
    };

    window.addEventListener('message', handleMessage);
    return () => window.removeEventListener('message', handleMessage);
  }, []);

  return (
    <>
      <button onClick={handleSetPIN}>Set Card PIN</button>

      {showModal && (
        <div className="modal">
          <iframe
            src={iframeUrl}
            title="Set Card PIN"
            style={{ width: '100%', height: '500px', border: 'none' }}
          />
        </div>
      )}
    </>
  );
}
```

## Customization Examples

### Modern Dark Theme

```json theme={null}
{
  "customCss": {
    "backgroundColor": "#1F2937",
    "textColor": "#F9FAFB",
    "backgroundColorPrimary": "#3B82F6",
    "textColorPrimary": "#FFFFFF",
    "buttonBorderRadius": 12,
    "pinBorderRadius": 8,
    "pinBorderColor": "#60A5FA"
  }
}
```

### Light Theme

```json theme={null}
{
  "customCss": {
    "backgroundColor": "#FFFFFF",
    "textColor": "#111827",
    "backgroundColorPrimary": "#10B981",
    "textColorPrimary": "#FFFFFF",
    "buttonBorderRadius": 8,
    "pinBorderRadius": 4
  }
}
```

### Brand Colors

```json theme={null}
{
  "customCss": {
    "backgroundColor": "#FEF3C7",
    "textColor": "#78350F",
    "backgroundColorPrimary": "#F59E0B",
    "textColorPrimary": "#FFFFFF",
    "buttonBorderRadius": 10,
    "pinBorderRadius": 6,
    "pinBorderColor": "#D97706"
  }
}
```

## PIN Requirements

<Note>
  **Standard PIN Format**

  * Must be exactly **4 digits**
  * Only numeric characters (0-9)
  * No letters or special characters
  * Cannot be all the same digit (e.g., 1111, 2222)
</Note>

The hosted page enforces these requirements automatically with real-time validation.

## Security Best Practices

<Warning>
  **Token Security**

  * Tokens expire after \~10 minutes
  * Single-use tokens become invalid after first access
  * Generate new tokens for each PIN setup attempt
  * Never store, cache, or log tokens
</Warning>

<Info>
  **PCI Compliance**

  Using this endpoint ensures PCI compliance as PIN data is created and stored entirely within secure, PCI-compliant systems. Your application never handles PIN values.
</Info>

<Note>
  **Secure Communication**

  All communication with the hosted page occurs over HTTPS with strong encryption. The PIN is never exposed in logs, analytics, or network traffic accessible to your application.
</Note>

## Common Use Cases

### Onboarding Flow

```typescript theme={null}
async function cardActivationFlow() {
  await orderCard();

  let card = await getCardStatus();
  while (card.status !== 'ACTIVE') {
    await delay(2000);
    card = await getCardStatus();
  }

  showSuccess('Card activated! Now set your PIN.');

  const { hostedPageUrl } = await generateSetPinToken({
    redirectUrl: `${window.location.origin}/dashboard`,
    isEmbedded: false
  });

  window.location.href = hostedPageUrl;
}
```

### Security Settings

```typescript theme={null}
function SecuritySettings() {
  return (
    <div>
      <h2>Card Security</h2>
      <button onClick={changePIN}>Change PIN</button>
      <button onClick={viewPIN}>View Current PIN</button>
      <button onClick={freezeCard}>Freeze Card</button>
    </div>
  );
}
```

## Error Handling

```typescript theme={null}
async function safelySetPIN() {
  try {
    const card = await getCardStatus();

    if (card.status !== 'ACTIVE') {
      throw new Error('Card must be active to set PIN');
    }

    const { hostedPageUrl } = await generateSetPinToken({
      redirectUrl: window.location.href,
      isEmbedded: false
    });

    window.location.href = hostedPageUrl;
  } catch (error) {
    if (error.response?.status === 404) {
      showError('No card found. Please order a card first.');
    } else if (error.response?.status === 401) {
      showError('Session expired. Please log in again.');
    } else {
      showError('Failed to set PIN. Please try again.');
    }
  }
}
```

## Related Endpoints

* `POST /v1/card/pin/token` - Generate token to view current PIN
* `POST /v1/card/details/token` - Generate token to view card details
* `GET /v1/card/status` - Check card status before PIN setup
* `POST /v1/card/order` - Order a new card
