## Overview

Customer intelligence is a suite of tools that allow you to build detailed profiles for your customers. By attaching customer data to your transactions, you gain valuable insights, reduce fraud risk, and create more personalized experiences.

Reservepay's intelligence engine automatically correlates information across your account. Whether you provide a Customer ID, an email address, or a mobile phone number, we'll correctly identify and link the data to the same customer profile.

## Managing contacts

Contacts are the core of customer intelligence in Reservepay. You can create, retrieve, update, and remove contacts using our API. Contacts support both payments and payouts, making it easy to manage the full lifecycle of your customer interactions.

### Creating a contact

To create a new contact, use the `merchants/add-contact` endpoint. You must provide either an email address or a mobile number. You can also provide your own `external_id` to use as a Customer ID from your system.

```ts
await fetch('https://api.reservepay.com/merchants/add-contact', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Api-Version': '2025-04-01',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    contact_email: 'customer@example.com',
    contact_name: 'John Doe',
    external_id: 'cust_987654' // Your own Customer ID
  })
})
```

## Linking transactions to contacts

When initiating a payment or a payout, you can link the transaction to a contact. Our system will automatically correlate the information even if you only provide an email or mobile number.

### Payments

When initiating a payment flow, you can link the transaction to an existing contact by providing the `customer_id` (either our `ctc_` ID or your `external_id`), `customer_email`, or `customer_mobile`.

```ts
await fetch('https://api.reservepay.com/merchants/initiate-payment-flow', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Api-Version': '2025-04-01',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    amount: '10000',
    currency: 'THB',
    customer_email: 'customer@example.com', // We'll automatically correlate this to the right contact
    payment_session_id: 'sess_1234567890',
    capture: true,
    return_url: 'https://example.com/return'
  })
})
```

### Payouts

You can also send payouts to your contacts. Use the `merchants/send-payout-to-contact` endpoint to send funds to a contact's primary bank account or via PromptPay using their mobile number or national ID.

## Managing bank accounts for contacts

To send a payout to a contact's bank account, you can first link one or more bank accounts to the contact using the `merchants/link-bank-account` endpoint.

Once a bank account is linked, you can:

- **Set a primary account**: Use `merchants/set-primary-contact-bank-account` to specify which account should receive payouts by default.
- **Find accounts**: Use `merchants/find-bank-account` to retrieve details of a specific bank account.
- **Unlink accounts**: Use `merchants/unlink-bank-account` to remove a bank account from a contact's profile.

## Saving cards for future use

You can securely save customer cards for future payments. This is useful for implementing one-click checkouts or subscription billing.

### Tokenizing a card

First, tokenize the card details using the `sdk/tokenize-card` endpoint. Set `usage: 'MULTIPLE'` to create a reusable token. A reusable token can be saved and charged again, but each customer-initiated payment still needs the CVV. See [The CVV on saved cards](#saved-card-cvv).

### Saving the card to a contact

Save a card only after the customer agrees. Each saved card gets a `card_id`. You can save it in two ways:

* **With the payment:** Add `customer_id` and `save_card` to your `merchants/initiate-payment-flow` call. Use this when the customer saves the card at checkout.
* **On its own:** Call `merchants/save-card` when no payment is in progress.

#### With the payment

Set `save_card` to `ADD` to attach the card to the customer, or `DEFAULT` to also make it their default card. The card is saved only if the payment's token was created with `usage: 'MULTIPLE'`.

```ts
await fetch('https://api.reservepay.com/merchants/initiate-payment-flow', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Api-Version': '2025-04-01',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    amount: '1000',
    currency: 'THB',
    payment_session_id: 'sess_1234567890',
    capture: true,
    return_url: 'https://example.com/return',
    external_id: 'order_12345',
    customer_id: 'ctc_1234567890',
    save_card: 'ADD'
  })
})
```

#### On its own

```ts
await fetch('https://api.reservepay.com/merchants/save-card', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Api-Version': '2025-04-01',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    token: 'ABCDEF123456789',
    contact_id: 'ctc_1234567890',
    default: true
  })
})
```

Only a token created with `usage: 'MULTIPLE'` can be saved. Saving a single-use token returns `NOT_REUSABLE`, and a token that was already used returns `TOKEN_ALREADY_USED`. In both cases, tokenize the card again.

### Retrieving saved cards

You don't need to store tokens yourself. Call `merchants/retrieve-contact` to list the contact's cards. Each active card includes:

* `token`: The merchant-scoped opaque token you pass to the browser SDK for a saved-card checkout.
* `card_id`: The ID your backend uses, for example with `merchants/request-payment`.

```ts
const contact = await fetch('https://api.reservepay.com/merchants/retrieve-contact', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Api-Version': '2025-04-01',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ contact_id: 'ctc_1234567890' })
}).then(res => res.json())

const savedCards = contact.default_card
  ? [
      contact.default_card,
      ...contact.cards.filter(
        card => card.card_id !== contact.default_card.card_id
      )
    ]
  : contact.cards

const savedCardTokens = savedCards.map(card => card.token)
```

The example puts the default card first because the SDK initially selects the first valid token. To look up one card by its ID or token, use `merchants/find-card`.

### Charging a saved card

You can charge a saved card in two ways:

| Flow | What the customer does | Checks | Use it for |
| :--- | :--- | :--- | :--- |
| **Customer-initiated payment (CIT)** | Picks the saved card in your checkout and enters the CVV again. | CVV, plus 3D Secure when it applies. | Returning customers on your checkout page. See [The CVV on saved cards](#saved-card-cvv). |
| **Merchant-initiated payment (MIT)** | Taps pay in your app, or nothing at all. | No CVV and no 3D Secure. Your backend charges the card directly. | One-tap payments in your own app, such as paying for an EV charging session, and charges without the customer, such as renewals. See [Merchant-initiated payments](#merchant-initiated-payments-mit). |

> {.required}
> **Required:** For a customer-initiated payment with the browser SDK, return the tokens from your backend and pass them to the SDK: `formOptions.savedCardTokens` for [pre-built forms](/guides/accept-payments-via-pre-built-forms#saved-card-pre-built), or the selected card's `token` as `savedCardToken` for [your own UI](/guides/accept-payments-using-your-own-ui#saved-card-own-ui).

#### The CVV on saved cards {#saved-card-cvv}

Reservepay never saves the CVV with the card. The CVV entered with `sdk/tokenize-card` covers only the payment made right after it. For any later customer-initiated payment, the saved card's `token` needs a newly entered CVV, added with `sdk/rehydrate-card`. That's why a saved card can go through when it's charged right after it's saved.

For every customer-initiated payment on a saved card:

1. Collect the CVV from the customer.
2. Call `sdk/rehydrate-card` with the saved card's `token` and the CVV. It returns the same `token`.
3. Right after, create the payment session with `sdk/select-payment-method` using that `token`, and have your backend call `merchants/initiate-payment-flow`.

If the payment fails, ask for the CVV and rehydrate the card again before retrying.

The browser SDK does steps 1 and 2 for you when you pass it saved-card tokens. It shows a CVV field, then calls `sdk/rehydrate-card`: [pre-built forms](/guides/accept-payments-via-pre-built-forms#saved-card-pre-built) do this before they create the payment session, and in [your own UI](/guides/accept-payments-using-your-own-ui#saved-card-own-ui), `instance.tokenize()` does it. Call `sdk/rehydrate-card` yourself only when you call the SDK API directly, without the browser SDK.

> {.required}
> **Required:** A saved card's `token` must be rehydrated before you pass it to `sdk/select-payment-method`. The browser SDK does this for you. If you call the SDK API directly, call `sdk/rehydrate-card` yourself. If you skip it, `sdk/select-payment-method` still creates the payment session, but the payment fails later. `usage: 'MULTIPLE'` means the card can be saved and reused. It doesn't let a customer-initiated payment skip the CVV. To charge a saved card without the CVV, use a [merchant-initiated payment](#merchant-initiated-payments-mit).

### Merchant-initiated payments (MIT)

Use `merchants/request-payment` to charge a saved card from your backend, with no payment session, SDK, CVV, or 3D Secure. The customer doesn't have to do anything at payment time, so it works for both:

* **One-tap payments in your app:** The customer signed in to your app and saved a card earlier. When they tap pay, for example to pay for an EV charging session, your app tells your backend, and your backend calls `merchants/request-payment`.
* **Charges without the customer:** Subscription renewals, or a final charge after a booking or service ends.

* Pass the contact's `card_id`, or leave it out to charge the contact's default card.
* Pass your `installation_id`, and identify the contact with `customer_id` (the contact ID or your own customer ID), `customer_email`, or `customer_mobile`. The card must belong to that contact.
* Ask Reservepay support to enable merchant-initiated payments on your card payment method. Until then, the call returns `NOT_ALLOWED`.

#### Example

```ts
await fetch('https://api.reservepay.com/merchants/request-payment', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Api-Version': '2025-04-01',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    amount: '1000',
    currency: 'THB',
    installation_id: 'ins_1234567890',
    capture: true,
    customer_id: 'ctc_1234567890',
    card_id: 'crd_1234567890' // Omit to charge the default card
  })
})
```

Since this **skips 3D Secure**, so they don't get the liability shift a 3D Secure challenge gives, and you carry the chargeback risk. You can still challenge a dispute with evidence. When the customer saves the card, get their permission to charge it this way, and keep a record of it to submit if they dispute a charge. Because your backend makes the charge, your app must confirm that the signed-in customer is the one paying before you call `merchants/request-payment`. See [Disputes](/guides/disputes).

## Insights and analytics

By consistently linking payments to contacts, you can unlock powerful insights through our dashboard and API. You can see:

- **Customer lifetime value (CLV):** Total amount spent by a customer.
- **Purchase frequency:** How often a customer makes a purchase.
- **Preferred payment methods:** Which payment methods a customer uses most frequently.
