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

# Tokens

> Securely tokenize card details from the browser

## Overview

Tokens allow you to securely collect card details on your own payment form. Card data is tokenized in the browser using the JavaScript SDK and your publishable key, so raw card numbers never reach your server.

## Create Token

`POST /v1/tokens`

**Authentication:** Publishable key (`pk_test_*` / `pk_live_*`) via Bearer token

<Note>
  You can call this endpoint directly with `fetch` or any HTTP client. The [JavaScript SDK](#javascript-sdk) is optional — it provides client-side validation and a simpler API, but is not required.
</Note>

#### Request Parameters

<ParamField body="card" type="object" required>
  Card details object

  <Expandable title="card properties">
    <ParamField body="number" type="string" required>
      Card number (13-19 digits). Spaces and dashes are automatically stripped.
    </ParamField>

    <ParamField body="exp_month" type="integer" required>
      Expiration month (1-12)
    </ParamField>

    <ParamField body="exp_year" type="integer" required>
      Expiration year (e.g., 2027)
    </ParamField>

    <ParamField body="cvc" type="string" required>
      Security code (3-4 digits)
    </ParamField>

    <ParamField body="cardholder_name" type="string">
      Cardholder name
    </ParamField>
  </Expandable>
</ParamField>

#### Response

<ResponseField name="id" type="string">
  Token ID (`tok_` prefix). Valid for 30 minutes, single use.
</ResponseField>

<ResponseField name="card" type="object">
  Masked card details

  <Expandable title="card properties">
    <ResponseField name="last4" type="string">
      Last 4 digits of the card number
    </ResponseField>

    <ResponseField name="brand" type="string">
      Card brand (`visa`, `mastercard`, `amex`, `discover`, `jcb`, `diners`)
    </ResponseField>

    <ResponseField name="exp_month" type="integer">
      Expiration month
    </ResponseField>

    <ResponseField name="exp_year" type="integer">
      Expiration year
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="created_at" type="string">
  Creation timestamp (ISO 8601)
</ResponseField>

<ResponseField name="expires_at" type="string">
  Expiration timestamp (ISO 8601). 30 minutes after creation.
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://api.sandbox.zafapay.com/v1/tokens \
    -H "Authorization: Bearer pk_test_xxxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "card": {
        "number": "4242424242424242",
        "exp_month": 12,
        "exp_year": 2027,
        "cvc": "123",
        "cardholder_name": "John Doe"
      }
    }'
  ```

  ```javascript Node.js theme={null}
  const response = await fetch('https://api.sandbox.zafapay.com/v1/tokens', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer pk_test_xxxxx',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      card: {
        number: '4242424242424242',
        exp_month: 12,
        exp_year: 2027,
        cvc: '123',
        cardholder_name: 'John Doe'
      }
    })
  });
  ```
</RequestExample>

<ResponseExample>
  ```json 201 Created theme={null}
  {
    "id": "tok_xxxxxxxxxxxxxxxxxxxxxx",
    "card": {
      "last4": "4242",
      "brand": "visa",
      "exp_month": 12,
      "exp_year": 2027
    },
    "created_at": "2026-04-07T10:30:00.000Z",
    "expires_at": "2026-04-07T11:00:00.000Z"
  }
  ```
</ResponseExample>

## Token Errors

| Code                         | HTTP Status | Description                                |
| ---------------------------- | ----------- | ------------------------------------------ |
| `validation_error`           | 400         | Invalid card details (number, expiry, CVC) |
| `tokenization_failed`        | 400         | Failed to tokenize card data               |
| `payment_token_expired`      | 400         | Token has expired (30 minute limit)        |
| `payment_token_already_used` | 400         | Token has already been used for a payment  |

## Using a Token for Payment

After creating a token, pass it to `POST /v1/payments`:

```json theme={null}
{
  "amount": 10.00,
  "currency": "usd",
  "token": "tok_xxxxxxxxxxxxxxxxxxxxxx",
  "return_url": "https://your-site.com/complete"
}
```

For the full integration guide, see [Server-to-Server Payments](/en/guide/s2s-payments).

<Tip>
  You can save the card for recurring payments by adding `save_card: true` and `customer_id` to the payment request. See [S2S Payments — Save Card](/en/guide/s2s-payments#save-card-for-recurring-payments) for details.
</Tip>

## JavaScript SDK

The JavaScript SDK is an optional convenience wrapper that provides client-side card validation and a simpler API.

```html theme={null}
<script src="https://js.zafapay.com/v1/zafapay.js"></script>
```

```javascript theme={null}
const zafapay = Zafapay('pk_test_xxxxx');

const { token, card, expires_at } = await zafapay.createToken({
  number: '4242424242424242',
  exp_month: 12,
  exp_year: 2027,
  cvc: '123',
  cardholder_name: 'John Doe'
});
// token: "tok_xxxxxxxxxxxxxxxxxxxxxx"
// card: { last4, brand, exp_month, exp_year }
```

<Info>
  The SDK returns the token ID as `token` (the API response field is `id` — the SDK renames it for convenience).
</Info>
