> For the complete documentation index, see [llms.txt](https://plexo.gitbook.io/rest-api/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://plexo.gitbook.io/rest-api/customers-and-saved-methods/tokenization-guide.md).

# Tokenization Guide

Save payment instruments for later use with the public sessions, customers, and payments APIs.

## Overview

The public API supports tokenization in three contract-backed ways:

* Create a hosted tokenization session with `POST /v1/sessions` and `"type": "tokenization"`
* Save a card during a payment by sending `paymentMethod.card.tokenizationSettings`
* Tokenize a wallet payment method (Google Pay or Apple Pay) directly via `POST /v1/tokenizations`

If you are integrating the hosted session flow specifically, start with [Tokenization Session Integration](/rest-api/sessions/tokenization-session.md).

If you want to reuse a saved card later, associate it with a customer and read the token from `GET /v1/customers/{customerId}/payment-instruments`.

The public contract does not document a standalone `/api/tokens` endpoint or a `saveCard` request flag.

If you are tokenizing wallet credentials (Google Pay or Apple Pay) with your own UI, see [Direct Wallet Tokenization](#direct-wallet-tokenization-google-pay--apple-pay) below.

Use this guide for collecting and storing reusable payment instruments. For customer CRUD, use [Customer Management](/rest-api/customers-and-saved-methods/customer-management.md). For recurring billing on top of saved instruments, use [Recurring Payments](/rest-api/customers-and-saved-methods/recurring-payments.md).

## Recommended Flow: Tokenization Session

Use a tokenization session when you need to save a card without charging it immediately.

### 1. Create a customer

Customers are optional for some flows, but they are the recommended way to manage reusable payment instruments.

```http
POST /v1/customers
Authorization: Basic {credentials}
Content-Type: application/json

{
  "referenceId": "customer-1001",
  "email": "customer@example.com",
  "firstName": "Test",
  "lastName": "User"
}
```

### 2. Create the tokenization session

```http
POST /v1/sessions
Authorization: Basic {credentials}
Content-Type: application/json

{
  "merchantId": 12345,
  "referenceId": "tok-session-1001",
  "type": "tokenization",
  "customerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "settings": {
    "tokenization": {
      "type": "store",
      "allowedCardTypes": ["credit", "debit"],
      "features": ["create", "list", "select", "delete"]
    },
    "callbacks": {
      "tokenizationCallbackUrl": "https://merchant.example.com/callbacks/tokenization"
    }
  }
}
```

Use the returned session data in your hosted flow. After completion, either consume the tokenization callback or inspect created sessions with `GET /v1/sessions` using your `referenceId` and pagination filters.

### Inspect created sessions

Use `GET /v1/sessions` when you need to audit or troubleshoot checkout and tokenization sessions created by your API key.

```http
GET /v1/sessions?merchantId=12345&referenceId=tok-session-1001&page=1&size=20
Authorization: Basic {credentials}
```

You can filter this list by `merchantId`, `referenceId`, `minDate`, and `maxDate`, then paginate with `page` and `size`.

### 3. List the saved instruments

```http
GET /v1/customers/3fa85f64-5717-4562-b3fc-2c963f66afa6/payment-instruments
Authorization: Basic {credentials}
```

Example response:

```json
[
  {
    "token": "9b7f1c2e-4a3d-4e8b-9c1f-2d3e4f5a6b7c",
    "name": "4242",
    "type": "credit",
    "issuer": {
      "id": 1,
      "name": "VISA"
    },
    "expMonth": 12,
    "expYear": 2028,
    "status": "Ok",
    "tokenizationType": "persistent"
  }
]
```

## Save a Card During the First Payment

If the first transaction should both charge the customer and save the card, create the payment with `paymentMethod.card.tokenizationSettings`.

```http
POST /v1/payments
Authorization: Basic {credentials}
Content-Type: application/json

{
  "merchantId": 12345,
  "referenceId": "order-1001",
  "invoiceNumber": "INV-1001",
  "customerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "amount": {
    "total": 10000,
    "currency": "UYU"
  },
  "paymentMethod": {
    "source": "card",
    "card": {
      "number": "4444333322221111",
      "expMonth": 12,
      "expYear": 2028,
      "cvc": "123",
      "cardholder": {
        "firstName": "Test",
        "lastName": "User",
        "email": "customer@example.com"
      },
      "tokenizationSettings": {
        "type": "store"
      }
    }
  },
  "capture": {
    "method": "automatic"
  }
}
```

When the payment succeeds, retrieve the saved token through the customer's payment instruments endpoint. Do not assume the payment response itself is the source of truth for stored instruments.

## Charge a Saved Instrument

Once you have the token, create a new payment with `paymentMethod.source = token`.

```http
POST /v1/payments
Authorization: Basic {credentials}
Content-Type: application/json

{
  "merchantId": 12345,
  "referenceId": "renewal-1001",
  "invoiceNumber": "INV-RENEWAL-1001",
  "customerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "amount": {
    "total": 10000,
    "currency": "UYU"
  },
  "paymentMethod": {
    "source": "token",
    "paymentInstrument": {
      "token": "9b7f1c2e-4a3d-4e8b-9c1f-2d3e4f5a6b7c"
    }
  },
  "capture": {
    "method": "automatic"
  }
}
```

Add `cvc` inside `paymentMethod.paymentInstrument` if your processor or risk policy requires it.

## Direct Wallet Tokenization (Google Pay / Apple Pay)

Use `POST /v1/tokenizations` when you collect Google Pay or Apple Pay wallet tokens with your own UI (e.g. the Google Pay or Apple Pay JavaScript SDKs) and want to tokenize them server-side without creating a hosted session.

This endpoint only accepts `google-pay-token` and `apple-pay-token` sources. Card tokenization must use sessions or the save-during-payment flow described above.

Wallet tokenizations are always enforced as `temporal` and expire after 24 hours, regardless of the value sent in `type`. They do not create permanent stored customer instruments.

### Google Pay example

Set `googlePayToken.paymentMethodData` to `JSON.stringify(paymentData.paymentMethodData)`. This required JSON string contains the complete method object serialized once; do not serialize the outer response or serialize the resulting string again. The decoded object requires `type: "CARD"`, `tokenizationData.type: "PAYMENT_GATEWAY"` and a nonempty string `tokenizationData.token`. Preserve the inner token string unchanged and do not Base64-encode either value. Raw objects and legacy token-only input are not accepted, including session flows. Optional `description` and `info` accept any JSON kind; malformed display content is ignored. See [GooglePayTokenDto](/rest-api/api-reference/models.md#googlepaytokendto) for the full schema. The wallet token placeholders in these examples cannot be charged.

```http
POST /v1/tokenizations
Authorization: Basic {credentials}
Content-Type: application/json

{
  "merchantId": 12345,
  "type": "temporal",
  "paymentMethod": {
    "source": "google-pay-token",
    "walletChannel": "api",
    "googlePayToken": {
      "paymentMethodData": "{\"type\":\"CARD\",\"description\":\"Visa •••• 0027\",\"info\":{\"cardNetwork\":\"VISA\",\"cardDetails\":\"0027\"},\"tokenizationData\":{\"type\":\"PAYMENT_GATEWAY\",\"token\":\"{\\\"signature\\\":\\\"PLACEHOLDER\\\",\\\"protocolVersion\\\":\\\"ECv2\\\",\\\"signedMessage\\\":\\\"PLACEHOLDER\\\"}\"}}",
      "cardholder": {
        "firstName": "Test",
        "lastName": "User",
        "email": "customer@example.com"
      }
    }
  }
}
```

### Apple Pay example

The input remains a string: serialize the entire `event.payment.token` wrapper once. Its optional `paymentMethod.displayName` supplies recognition hints. Existing paymentData-only input remains accepted, but supplies no display hints.

```http
POST /v1/tokenizations
Authorization: Basic {credentials}
Content-Type: application/json

{
  "merchantId": 12345,
  "type": "temporal",
  "paymentMethod": {
    "source": "apple-pay-token",
    "walletChannel": "api",
    "applePayToken": {
      "token": "{\"paymentData\":{\"data\":\"PLACEHOLDER\",\"header\":{},\"signature\":\"PLACEHOLDER\",\"version\":\"EC_v1\"},\"paymentMethod\":{\"displayName\":\"Visa •••• 0027\"}}",
      "cardholder": {
        "firstName": "Test",
        "lastName": "User",
        "email": "customer@example.com"
      }
    }
  }
}
```

### Response

The response is a `PaymentInstrumentTokenDto` with card metadata including `cardType` (credit, debit, prepaid), `cardOrigin` (uruguay, international), and issuer details:

```json
{
  "token": "9b7f1c2e-4a3d-4e8b-9c1f-2d3e4f5a6b7c",
  "paymentMethodId": "visa",
  "paymentMethodType": "card",
  "tokenizationType": "temporal",
  "status": "active",
  "source": "network-token",
  "bin": "41111111",
  "last4": "1111",
  "metadata": {
    "display_name": "Visa •••• 0027",
    "display_last4": "0027"
  },
  "expMonth": 12,
  "expYear": 2030,
  "cardName": "411111XXXXXX1111",
  "cardholderName": "John Doe",
  "cardType": "credit",
  "cardOrigin": "uruguay",
  "issuer": {
    "id": 101,
    "name": "Banco República Oriental del Uruguay",
    "shortName": "BROU",
    "country": "Uruguay",
    "countryCode": "UY"
  },
  "createdAt": "2025-01-15T10:30:00Z",
  "expiresAt": "2025-01-16T10:30:00Z"
}
```

Use `cardType` and `cardOrigin` (or `issuer.countryCode` when present) to decide which installment options to display in your UI. The endpoint does not calculate installment plans; use the metadata combined with your own merchant configuration for that logic.

### Tokenization types

| Type       | Behavior                                                                           |
| ---------- | ---------------------------------------------------------------------------------- |
| `temporal` | Expires automatically after 24 hours. Use for immediate payment flows.             |
| `store`    | Persists until deleted. Requires a `customerId` for the customer instruments list. |

## Wallet Display Hints

Current instrument-token responses and saved-wallet instrument/session selection expose optional `metadata.display_name` and `metadata.display_last4`. Payment responses and current payment callbacks use `paymentMethod.card.metadata`; this card dictionary contains only those two safe display keys. Tokenization callbacks preserve PascalCase: `Data.PaymentInstrument.Metadata`, with literal `display_name` and `display_last4` keys. See [callbacks](/rest-api/operations/callbacks.md).

| Key             | Google Pay source                    | Apple Pay source                                                               | Returned value                                                                        |
| --------------- | ------------------------------------ | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- |
| `display_name`  | `paymentMethodData.description`      | `paymentMethod.displayName`                                                    | Trimmed safe label, at most 128 characters; empty, unsafe or malformed labels omitted |
| `display_last4` | `paymentMethodData.info.cardDetails` | Four ASCII digits extracted from `paymentMethod.displayName`, when unambiguous | Exactly four ASCII digits as a string; leading zeros preserved                        |

* These are client-provided recognition hints, not authenticated or verified card identity and not a guarantee of the underlying physical card number. Render the label as text, not HTML.
* Missing or malformed keys are omitted independently. When neither exists, payment `card.metadata` is omitted; other existing instrument metadata can still be present.
* Processing `last4` is unchanged and can differ from `display_last4` (`"1111"` versus `"0027"` above). For wallet UI, use only normalized `display_last4`. If absent, show no suffix; never fall back to the network-token suffix or parse digits from the label. Ordinary card display is unchanged.
* Payments using a saved wallet instrument reuse its stored hints, not caller-supplied replacements. Do not set reserved display keys in generic request metadata.
* Historical records may have no hints. Legacy customer-instrument APIs, legacy transaction callbacks and VTEX callbacks are unchanged; do not assume they gain these fields.

## Manage Saved Instruments

The public customer instrument surface is intentionally small:

* List instruments: `GET /v1/customers/{customerId}/payment-instruments`
* Delete an instrument: `DELETE /v1/customers/{customerId}/payment-instruments/{tokenId}`

The public API does not currently document a "set default instrument" operation.

## Best Practices

* Create a customer before tokenization if the card should be reusable
* Use tokenization sessions when you do not need an immediate charge
* Treat the customer payment instruments endpoint as the canonical source for reusable tokens
* Do not store PAN or CVV in your own systems
* Use distinct `referenceId` values for the tokenization step and each later charge

## Related Resources

* [Customer Management](/rest-api/customers-and-saved-methods/customer-management.md)
* [Recurring Payments](/rest-api/customers-and-saved-methods/recurring-payments.md)
* [PaymentRequestDto](/rest-api/api-reference/models.md#paymentrequestdto)
* [SessionRequest](/rest-api/api-reference/models.md#sessionrequest)
