> For the complete documentation index, see [llms.txt](https://docs.tendar.co/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tendar.co/documentation/recollection/card.md).

# Card

Securely save a customer's card for future charges — without handling raw card details yourself.

If you're building a product that charges customers more than once — loan repayments, subscription renewals, marketplace payouts — you need a way to save their card on file. But storing card numbers directly is a security and compliance nightmare. That's where tokenization comes in.

The Card API lets you create a secure checkout session where your customer enters their card details on a hosted page managed by Payment Providers. Once they complete the flow, Tendar stores a reusable token that represents their card. From that point on, you can charge the customer using the token — no card numbers, no PCI scope, no redirects.

Think of it this way: the customer enters their card once, and you can charge it as many times as you need.

### Before you begin

***

Make sure you have:

* A **user** already created through the [Onboarding Service](/documentation/onboarding/overview.md). Each beneficiary is linked to a user by their `user_id` (e.g. `usr-cheltgfnqt`).
* Your **secret key** from the [Tendar dashboard](https://app.tendar.co/services?service-level=api-keys-and-webhooks). All requests require a Bearer token:

  ```
  Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx
  ```

### The card object

***

Here's a breakdown of every field on the card object and what it means:

| Field                | Type    | Description                                                                              |
| -------------------- | ------- | ---------------------------------------------------------------------------------------- |
| `id`                 | string  | The unique identifier for this card record.                                              |
| `user`               | string  | The internal user ID (MongoDB ObjectId).                                                 |
| `user_id`            | string  | The Tendar user ID (e.g., `usr-cheltgfnqt`).                                             |
| `default`            | boolean | Whether this is the customer's default card.                                             |
| `payment_service`    | string  | The payment provider used to tokenize the card (`paystack`, `flutterwave`, or `stripe`). |
| `valid`              | boolean | Whether the card has been verified. Only valid cards can be charged.                     |
| `currency`           | string  | The currency associated with the card.                                                   |
| `card.last_4_digits` | string  | The last 4 digits of the card number.                                                    |
| `card.bin`           | string  | The bank identification number (first 6 digits).                                         |
| `card.card_type`     | string  | The card brand — `visa`, `mastercard`, `verve`, etc.                                     |
| `card.bank`          | string  | The issuing bank name.                                                                   |
| `card.country_code`  | string  | The 2-letter country code (e.g., `NG`, `US`).                                            |
| `card.exp_month`     | string  | The card's expiration month.                                                             |
| `card.exp_year`      | string  | The card's expiration year.                                                              |
| `card.email`         | string  | The email address associated with the card (pulled from the user record).                |
| `signature`          | string  | A unique identifier for the physical card, used for deduplication.                       |
| `service_ref`        | string  | The payment provider's reference for the tokenization session.                           |

### How card tokenization works

***

The tokenization flow has three stages: **create**, **authorize**, and **verify**. Here's what happens at each stage, and what your application needs to do.

#### Step 1: Create a card

To start tokenizing a card, send a `POST` request to the create endpoint with the customer's `user_id`, the `currency`, and a `checkout` object containing redirect URLs.

```
POST /api/v1/card/create
```

```json
{
    "user_id": "usr-cheltgfnqt",
    "currency": "NGN",
    "checkout": {
        "success_url": "https://yourapp.com/card/success",
        "cancel_url": "https://yourapp.com/card/cancel"
    },
    "payment_service": ""
}
```

| Field                  | Type   | Required | Description                                                                                                        |
| ---------------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------ |
| `user_id`              | string | Yes      | The Tendar user ID of the customer whose card you want to tokenize.                                                |
| `currency`             | string | Yes      | The currency for the card. Currently supports `NGN` and `USD`.                                                     |
| `checkout.success_url` | string | Yes      | Where to redirect the customer after they successfully enter their card details.                                   |
| `checkout.cancel_url`  | string | No       | Where to redirect the customer if they cancel the checkout flow.                                                   |
| `payment_service`      | string | No       | Which payment provider to use (`paystack`, `flutterwave`, or `stripe`). Leave empty to use your company's default. |

When the request succeeds, Tendar does the following behind the scenes:

1. Validates that the user exists under your company.
2. Initializes a checkout session with a payment provider — this typically involves a small charge (e.g., ₦50) to verify the card is real.
3. Returns a card object with a `checkout.url` — this is the hosted page where your customer enters their card details.

Here's what the response looks like:

```json
{
    "data": {
        "created_at": "2024-07-03T20:15:58.574Z",
        "updated_at": "2024-07-03T20:15:58.574Z",
        "id": "6685b17e80d19519f2b34217",
        "user": "660f7665740ffdf6498d05f5",
        "user_id": "usr-cheltgfnqt",
        "default": false,
        "payment_service": "paystack",
        "valid": false,
        "currency": "NGN",
        "card": {
            "last_4_digits": "",
            "card_holder_name": "",
            "bin": "",
            "card_type": "",
            "bank": "",
            "country_code": "",
            "exp_month": "",
            "exp_year": "",
            "email": "john@example.com"
        },
        "signature": "",
        "service_ref": "ttpxasbpam",
        "checkout": {
            "success_url": "https://yourapp.com/card/success",
            "cancel_url": "https://yourapp.com/card/cancel",
            "url": "https://checkout.paystack.com/91p42szfrrujgn1"
        }
    },
    "error": false,
    "message": "Card created successfully"
}
```

Notice that `valid` is `false` and the `card` details are empty. The card hasn't been authorized yet — the customer still needs to complete the checkout.

**What to do next:** Redirect your customer to the `checkout.url`. This takes them to a secure, provider-hosted page where they enter their card number, expiry date, and CVV.

#### Step 2: Customer authorizes the card

This step happens entirely on the payment provider's hosted page — your application is not involved. The customer enters their card details, and the provider processes a small verification charge to confirm the card is valid and belongs to the customer.

Once the customer completes the flow, two things happen:

1. The customer is redirected to your `success_url` (or `cancel_url` if they abandoned the checkout).
2. The payment provider sends a webhook to Tendar notifying it that the card authorization is complete.

You don't need to call any API during this step. Just make sure your `success_url` shows the customer an appropriate message (e.g., "Card saved successfully" or "Processing your card...").

#### Step 3: Tendar verifies and saves the card

When Tendar receives the webhook from the payment provider, it automatically:

1. **Verifies the charge** — confirms that the verification payment succeeded.
2. **Extracts the card details** — pulls the last 4 digits, card type (Visa, Mastercard, etc.), issuing bank, expiry date, and country code from the provider's response.
3. **Generates a token** — stores the reusable token that allows future charges on this card without the customer re-entering details.
4. **Checks for duplicates** — if the customer already has a card with the same signature (i.e., the same physical card), Tendar updates the existing token instead of creating a duplicate. The new card record is marked as invalid with the message "card already exists."
5. **Sets the default** — if this is the customer's first valid card, it's automatically set as the default card.
6. **Refunds the verification charge** — the small amount charged during checkout is automatically refunded to the customer. This happens asynchronously — you don't need to trigger it.
7. **Sends a webhook** — fires a `card.validation.success` or `card.validation.failed` event to your company's registered webhook URL.

After verification, the card object looks like this:

```json
{
    "id": "66859a3ca53c1ca520a7b26d",
    "user": "660f7665740ffdf6498d05f5",
    "user_id": "usr-cheltgfnqt",
    "default": true,
    "payment_service": "paystack",
    "valid": true,
    "currency": "NGN",
    "card": {
        "last_4_digits": "4081",
        "bin": "408408",
        "card_type": "visa",
        "bank": "TEST BANK",
        "country_code": "NG",
        "exp_month": "12",
        "exp_year": "2030",
        "email": "john@example.com"
    },
    "signature": "SIG_gjjA8XmsnuQUmVKgnopy",
    "service_ref": "qkdc9pu7so"
}
```

The card is now `valid: true` and ready to be charged.

#### What if the card isn't verified in time?

If a customer starts the tokenization flow but never completes the checkout, the card sits in an invalid state. Tendar automatically cleans up these orphaned cards — any card that remains unverified after 24 hours is deleted. You don't need to handle this yourself.

### Managing cards

***

Once cards are tokenized, you have full control over listing, fetching, setting defaults, and deleting them.

#### List a customer's cards

Retrieve all cards belonging to a specific user. The response excludes sensitive fields like the token, checkout details, and payment information.

```
GET /api/v1/card/list/:user_id
```

You can filter results by passing query parameters:

| Parameter         | Type    | Description                                                               |
| ----------------- | ------- | ------------------------------------------------------------------------- |
| `valid`           | boolean | Filter by validation status. Use `valid=true` to get only verified cards. |
| `sort-created_at` | string  | Sort by creation date. Use `asc` or `desc`.                               |

Example response:

```json
{
    "data": [
        {
            "created_at": "2024-07-03T18:36:44.647Z",
            "updated_at": "2024-07-03T18:36:58.538Z",
            "id": "66859a3ca53c1ca520a7b26d",
            "user": "660f7665740ffdf6498d05f5",
            "user_id": "usr-cheltgfnqt",
            "default": true,
            "payment_service": "paystack",
            "valid": true,
            "currency": "NGN",
            "card": {
                "last_4_digits": "4081",
                "bin": "408408",
                "card_type": "visa",
                "bank": "TEST BANK",
                "country_code": "NG",
                "exp_month": "12",
                "exp_year": "2030",
                "email": "john@example.com"
            },
            "signature": "SIG_gjjA8XmsnuQUmVKgnopy",
            "service_ref": "qkdc9pu7so"
        }
    ],
    "error": false,
    "message": "Cards retrieved successfully"
}
```

#### Fetch a single card

Retrieve a specific card by its ID.

```
GET /api/v1/card/fetch/:id
```

#### Set a default card

When a customer has multiple cards, you can designate one as the default. The default card is the one that gets charged when you don't specify a card ID in a charge or recollection request.

```
PUT /api/v1/card/default/set/:id
```

Only verified cards can be set as default. If the card hasn't been validated yet, the request will fail with a `400` error.

When you set a new default, the previous default card is automatically unset — there's always exactly one default card per customer.

#### Delete a card

Remove a card from a customer's account. This deletes the card record from Tendar and deauthorizes the token with the payment provider.

```
DELETE /api/v1/card/delete/:id
```

By default, you cannot delete a customer's default card — this prevents accidentally removing the card your automated charges depend on. If you really need to delete the default card, pass the `delete_default=true` query parameter:

```
DELETE /api/v1/card/delete/:id?delete_default=true
```

When a card is deleted, Tendar also sends a deauthorization request to the payment provider, revoking the token so it can no longer be used.

### Charging a tokenized card

***

Once a card is tokenized and verified, you can charge it in two ways:

#### Through a recollection

The most common use case. When recording a loan repayment, set `payment_channel` to `"card"` and pass the card ID. Tendar charges the card and applies the repayment to the loan in a single step.

```json
POST /recollection/create

{
    "loan": "6685d6600defdf0d7eea6699",
    "amount": 3500,
    "with_provider": true,
    "payment_channel": "card",
    "card": "66859a3ca53c1ca520a7b26d",
    "callback_url": "https://yourapp.com/webhooks/tendar"
}
```

If you omit the `card` field, Tendar charges the customer's default card. See [Recollection](/documentation/recollection/recollection.md) guide for the full recollection flow.

#### Through a collect plan

If you're using the [Collect](broken://pages/ac31ecfbf9d5da3f6647d1f9a1a2bee3368deb7c) feature for installment-based collections, Tendar automatically charges the customer's default card on the scheduled debit dates. You don't need to initiate individual charges — the system handles it.

### Webhooks

***

The Card API sends webhook events at key moments in the card lifecycle. These are delivered to your company's registered webhook URL.

| Event                     | When it fires                                                     |
| ------------------------- | ----------------------------------------------------------------- |
| `card.validation.success` | The card has been verified, tokenized, and is ready for charges.  |
| `card.validation.failed`  | The verification charge failed — the card could not be tokenized. |

The webhook payload contains the full card object (excluding the token and checkout details), so you can update your UI or trigger downstream workflows without making an additional API call.

### Error handling

***

Here are the errors you're most likely to encounter when working with the Card API, and how to resolve them:

| Error                                       | HTTP Status | Cause                                                                                  | Resolution                                                                                           |
| ------------------------------------------- | ----------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `user not found`                            | 404         | The `user_id` doesn't match any user in your company.                                  | Verify the user ID and ensure the user has been created under your company.                          |
| `card not found`                            | 404         | The card ID doesn't exist or doesn't belong to your company.                           | Verify the card ID.                                                                                  |
| `card already verified`                     | 400         | You attempted to verify a card that has already been validated.                        | No action needed — the card is already usable.                                                       |
| `payment still pending`                     | 400         | The verification charge hasn't settled yet.                                            | Wait for the webhook or retry after a short delay.                                                   |
| `card already exists`                       | —           | The customer already has a verified card with the same signature (same physical card). | The existing card's token is updated. The duplicate card record is marked invalid. No action needed. |
| `only validated card can be set as default` | 400         | You tried to set an unverified card as default.                                        | Wait for the card to be verified first, then set it as default.                                      |
| `default card cannot be deleted`            | 400         | You tried to delete the customer's default card without explicitly allowing it.        | Pass `delete_default=true` as a query parameter, or set a different card as default first.           |

## Security and compliance

The Card API is designed so that you never handle raw card details:

* **Card numbers, CVVs, and full expiry dates never pass through your server.** The customer enters them on a hosted checkout page managed by the payment provider.
* **Tokens are stored encrypted** and scoped to your company. A token from one company cannot be used by another.
* **Verification charges are automatically refunded**, so tokenization doesn't cost your customer anything.
* **Orphaned cards are auto-cleaned.** If a customer starts the flow but doesn't complete it, the invalid card record is automatically deleted after 24 hours.

## Putting it all together

Here's a typical integration flow for saving and charging a customer's card:

1. **Create a card** — Call `POST /card/create` with the customer's `user_id`, `currency`, and `checkout` URLs. You'll get back a `checkout.url`.
2. **Redirect the customer** — Send the customer to the `checkout.url`. They enter their card details on the provider's hosted page.
3. **Wait for the webhook** — Tendar receives the provider's webhook, verifies the card, saves the token, and refunds the verification charge. You receive a `card.validation.success` event.
4. **Display the saved card** — Call `GET /card/list/:user_id?valid=true` to show the customer their saved cards. Display the last 4 digits, card type, and bank — never the full card number.
5. **Charge when needed** — Use the card ID in a [recollection](/documentation/recollection/recollection.md) (`payment_channel: "card"`) or let a [collect](broken://pages/ac31ecfbf9d5da3f6647d1f9a1a2bee3368deb7c) plan charge the default card automatically.
6. **Let the customer manage their cards** — Provide UI to set a different default (`PUT /card/default/set/:id`) or remove a card (`DELETE /card/delete/:id`).

## Next steps

* [Recollect Loan](broken://pages/74cc1c523967c6c9d6d6cd91199973a870b1df97) — Charge a saved card to record a loan repayment.
* [Direct Debit](broken://pages/3d226a9c88be769e75abfec694b3e156b07e0163) — Set up bank account authorizations as an alternative to cards.
* [Accept Payments](broken://pages/0d2f31d7eafbde462c4bbc361a4f79beb9f2eade) — Initialize one-time payment sessions.
* [Collect](broken://pages/ac31ecfbf9d5da3f6647d1f9a1a2bee3368deb7c) — Automate installment-based collections with scheduled card charges.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.tendar.co/documentation/recollection/card.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
