# Card Validation

### Overview

The Card Validation endpoint allows you to perform card verification with fraud prevention in a single Firstoken API call.&#x20;

### What Happens Internally

```
Client Request → Firstoken API → [Tokenization + Antifraud + Validation] → Response
```

1. **Tokenization**: Card data is tokenized with KOIN
2. **Anti-fraud validation**
3. **Validation** with Zero Amount Transaction
4. **Response**: Transaction details returned

This process validates the card's authenticity while providing fraud risk assessment, without permanently charging the cardholder.

### Request Body

The request format is identical to the Authorization endpoint, but with `"type": "validation"` instead of `"type": "authorization"`.

***

#### Response Flow

The validation process can result in different outcomes:

```
┌─────────────────────────────────────────┐
│     Card Validation Request Sent        │
└───────────────┬─────────────────────────┘
                │
                ▼
┌─────────────────────────────────────────┐
│      1. Tokenization with KOIN          │
└───────────────┬─────────────────────────┘
                │
                ▼
┌─────────────────────────────────────────┐
│      2. Anti-Fraud Validation           │
└───────────────┬─────────────────────────┘
                │
        ┌───────┴────────┐
        │                │
        ▼                ▼
   ✅ Approved      ❌ Denied
        │                │
        │                └─────► status: "Denied"
        │                        risk_info.status: "Denied"
        │                        
        │
        ▼
┌─────────────────────────────────────────────────────┐
│      3. Validation with Zero Amount Transaction     │
└───────────────┬─────────────────────────────────────┘
                │
        ┌───────┴────────┐
        │                │
        ▼                ▼
   ✅ Validated      ❌ Declined
                         │
                         └─────► status: "Declined"
                                 risk_info.status: "Approved"
                                 error_info with status reason
                                 (e.g , ExpiredCard)

```

***

### Endpoint

**POST** `/v1/payments/`

***

### Field Specifications

#### transaction\_info (Required)

| Parameter        | Type   | Required | Description                                         |
| ---------------- | ------ | -------- | --------------------------------------------------- |
| `type`           | string | Yes      | Must be `"validation"` for card validation requests |
| `reference_code` | string | Yes      | Unique reference code for the transaction           |

#### card (Required)

| Parameter         | Type   | Required | Description                               |
| ----------------- | ------ | -------- | ----------------------------------------- |
| `number`          | string | Yes      | Card number (PAN)                         |
| `expiration_date` | string | Yes      | Card expiration date in format MM/YYYY    |
| `security_code`   | string | Yes      | Card CVV/CVC security code                |
| `holder`          | string | Yes      | Cardholder name as it appears on the card |

{% hint style="warning" %}

#### Note: Use a Firstoken token instead of card details; simply apply the token directly as follow

* Temporal token:&#x20;

  ```postman_json
  "{{08d40297-29d3-493e-b790-8e1085bc0a11: transaction}}"
  ```
* Permanent token (you can use all schema token types) :

  ```postman_json
  "{{419894dd-4211-409b-a31e-c76aba8da9f3: detokenize}}"
  ```

{% endhint %}

#### Order Info

| Parameter                     | Type   | Required | Description                                                 |
| ----------------------------- | ------ | -------- | ----------------------------------------------------------- |
| `amount_details`              | object | Yes      | Contains amount and currency information                    |
| `amount_details.total_amount` | number | Yes      | Total amount for validation (typically 1 or minimal amount) |
| `amount_details.currency`     | string | Yes      | Currency code (e.g., "BRL", "USD", "COP")                   |
| `amount_details.items_amount` | number | Yes      | Items amount                                                |
| `installments`                | number | Yes      | Number of installments (use 1 for validation)               |
| `descriptor`                  | string | Yes      | Description that appears on statement                       |

#### Cardholder Info

| Parameter              | Type   | Required | Description                              |
| ---------------------- | ------ | -------- | ---------------------------------------- |
| `first_name`           | string | Yes      | Cardholder first name                    |
| `last_name`            | string | Yes      | Cardholder last name                     |
| `document`             | object | Yes      | Document information                     |
| `document.type`        | string | Yes      | Document type (e.g., "CPF", "CC", "DNI") |
| `document.number`      | string | Yes      | Document number                          |
| `document.nationality` | string | Yes      | Nationality code (ISO 3166-1 alpha-2)    |
| `country`              | string | Yes      | Country code (ISO 3166-1 alpha-2)        |
| `address_1`            | string | Yes      | Primary address line                     |
| `city`                 | string | Yes      | City name                                |
| `state`                | string | Yes      | State or province code                   |
| `postal_code`          | string | Yes      | Postal/ZIP code                          |
| `email`                | string | Yes      | Email address                            |
| `phone_number`         | string | Yes      | Phone number                             |
| `phone_type`           | string | Yes      | Phone type (e.g., "Mobile", "Home")      |
| `phone_area_code`      | string | Yes      | Phone area code                          |

#### Bill To

Contains billing information with the same structure as cardholder\_info, plus:

| Parameter | Type   | Required | Description        |
| --------- | ------ | -------- | ------------------ |
| `id`      | string | Yes      | Billing ID         |
| `profile` | string | Yes      | Profile identifier |

#### Line Items

Array of purchase items (at least one required):

| Parameter       | Type   | Required | Description                 |
| --------------- | ------ | -------- | --------------------------- |
| `category`      | object | Yes      | Item category information   |
| `category.id`   | string | Yes      | Category ID                 |
| `category.name` | string | Yes      | Category name               |
| `id`            | string | Yes      | Item ID                     |
| `name`          | string | Yes      | Item name                   |
| `price`         | number | Yes      | Item unit price             |
| `quantity`      | number | Yes      | Quantity                    |
| `type`          | string | Yes      | Item type (e.g., "Generic") |

#### Device Info

<table><thead><tr><th width="286.38671875">Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td><code>ip_address</code></td><td>string</td><td>Yes</td><td>Customer's IP address</td></tr><tr><td><code>fingerprint_session_id</code>*</td><td>string</td><td>Yes </td><td>A unique numerical ID generated for each user session on a website.</td></tr></tbody></table>

<p align="center"><sub>(*) The <code>fingerprint_session_id</code> is generated using a Koin script that is called from the webpage.</sub></p>

***

### Example Request

```bash
curl --location 'https://api.firstoken.co/v1/payments' \
--header 'Content-Type: application/json' \
--header 'x-api-key: YOUR_API_KEY' \
--data-raw '{
  "transaction_info": {
    "type": "validation",
    "reference_code": "1753550023570"
  },
  "card": {
    "number": "4225289837059229",
    "expiration_date": "01/2030",
    "security_code": "123",
    "holder": "John Doe Jones"
  },
  "order_info": {
    "amount_details": {
      "total_amount": 1,
      "currency": "BRL",
      "items_amount": 1
    },
    "installments": 1,
    "descriptor": "Test Payment"
  },
  "cardholder_info": {
    "first_name": "John",
    "last_name": "Doe",
    "document": {
      "type": "CPF",
      "number": "54672396002",
      "nationality": "BR"
    },
    "country": "BR",
    "address_1": "123 Main St",
    "city": "Sao Paulo",
    "state": "SP",
    "postal_code": "04001-000",
    "email": "john@test.com",
    "phone_number": "4158880000",
    "phone_type": "Mobile",
    "phone_area_code": "22"
  },
  "bill_to": {
    "id": "1234567890",
    "first_name": "John",
    "last_name": "Doe",
    "document": {
      "type": "CPF",
      "number": "54672396002",
      "nationality": "BR"
    },
    "country": "BR",
    "address_1": "123 Main St",
    "city": "Sao Paulo",
    "state": "SP",
    "postal_code": "04001-000",
    "email": "test@cybs.com",
    "phone_number": "4158880000",
    "phone_type": "Mobile",
    "phone_area_code": "22",
    "profile": "1"
  },
  "line_items": [
    {
      "category": {
        "id": "1",
        "name": "Category 2"
      },
      "id": "1",
      "name": "Item ",
      "price": 1500,
      "quantity": 1,
      "type": "Generic"
    }
  ],
  "device_info": {
    "ip_address": "127.0.0.1",
    "fingerprint_session_id": "cf01cee443d8529475f4841a3a78348c"
  }
}'
```

***

### Response Structure

#### Success Response - Card Validated

```json
{
  "status": "success",
  "message": "Validation successful",
  "data": {
    "transaction_info": {
      "type": "validation_response",
      "reference_code": "1753550023570",
      "transaction_id": "0001761666589459054367",
      "request_id": "6d488360-ce28-47c7-9722-6bebd1c5d8c9",
      "status": "Validated",
      "response_code": "777775",
      "created_at": "2025-10-28T15:49:57.331Z"
    },
    "order_info": {
      "amount_details": {
        "authorized_amount": 1,
        "currency": "BRL"
      }
    },
    "risk_info": {
      "status": "Approved",
      "date": "2025-10-28T15:50:03.000Z",
      "strategies": []
    }
  }
}
```

#### Declined Response - Authorization Failed

When the card passes anti-fraud validation but the authorization is declined (e.g., insufficient funds), no void is generated since authorization was not completed:

```json
{
  "status": "success",
  "message": "Validation failed",
  "data": {
    "transaction_info": {
      "type": "validation_response",
      "reference_code": "1753550023570",
      "transaction_id": "0001761674061485235739",
      "request_id": "de70f9d6-b85b-49dd-b205-3f374ed9d7de",
      "status": "Declined",
      "created_at": "2025-10-28T17:54:27.272Z"
    },
    "order_info": {
      "amount_details": {
        "authorized_amount": 10001,
        "currency": "BRL"
      }
    },
    "risk_info": {
      "status": "Approved",
      "date": "2025-10-28T17:54:26.000Z",
      "strategies": []
    },
    "error_info": {
      "reason": "ExpiredCard",
      "message": "Transaction failed"
    }
  }
}
```

> **Note:** When `transaction_info.status` is "Declined", there will be no `void_info` in the response because the authorization was not completed. An `error_info` object will be present instead with the reason for the decline.

#### Response Fields

**Transaction Info**

| Field            | Type   | Description                                    |
| ---------------- | ------ | ---------------------------------------------- |
| `type`           | string | Response type: "validation\_response"          |
| `reference_code` | string | Your original reference code                   |
| `transaction_id` | string | Unique transaction identifier assigned by KOIN |
| `request_id`     | string | Unique request identifier                      |
| `status`         | string | Validation status: "Validated" or "Declined"   |
| `response_code`  | string | Response code from the processor               |
| `created_at`     | string | Timestamp of transaction creation (ISO 8601)   |

**Order Info**

| Field                              | Type   | Description                |
| ---------------------------------- | ------ | -------------------------- |
| `amount_details.authorized_amount` | number | Amount that was authorized |
| `amount_details.currency`          | string | Currency code              |

**Risk Info**

Contains anti-fraud analysis results:

| Field        | Type   | Description                                    |
| ------------ | ------ | ---------------------------------------------- |
| `status`     | string | Risk assessment result: "Approved" or "Denied" |
| `date`       | string | Timestamp of risk analysis (ISO 8601)          |
| `strategies` | array  | Array of risk strategies applied (if any)      |

**Error Info**

Contains error details when authorization is declined (only present when `transaction_info.status` is "Declined"):

| Field     | Type   | Description                                                      |
| --------- | ------ | ---------------------------------------------------------------- |
| `reason`  | string | Reason code for the decline (e.g., "CardExpired", "InvalidCard") |
| `message` | string | Human-readable error message                                     |

***

### Response Status Values

#### Transaction Status

* **Validated**: Card validation successful, authorization completed and voided
* **Failed**: Card validation failed OR authorization was declined
  * When failed with `risk_info.status: "Approved"`: Validation failed (e.g., insufficient funds, card blocked)
  * When failed with `risk_info.status: "Denied"`: Anti-fraud checks failed

#### Risk Status

* **Approved**: Passed anti-fraud checks
* **Denied**: Failed anti-fraud checks

#### Error Reasons (in error\_info.reason)

Common decline reasons when validation fails:

| Reason              | Description                                         |
| ------------------- | --------------------------------------------------- |
| `CardExpired`       | Card expiration date has passed                     |
| `InvalidCard`       | Card number is invalid or card is not active        |
| `CardBlocked`       | Card has been blocked by the issuer                 |
| `ExceedsLimit`      | Transaction exceeds card limit                      |
| `IssuerUnavailable` | Card issuer system is temporarily unavailable       |
| `StolenCard`        | Card has been reported as stolen                    |
| `LostCard`          | Card has been reported as lost                      |
| `RestrictedCard`    | Card has restrictions that prevent this transaction |

***

### Error Handling

#### Error Response Example

```json
{
  "status": "fail",
  "message": "Invalid request",
  "data": {
    "description": "Must be a valid credit card number or token",
    "path": "card.number"
  }
}
```

#### Error Response Fields

| Field              | Type   | Description                       |
| ------------------ | ------ | --------------------------------- |
| `status`           | string | Always "fail" for error responses |
| `message`          | string | General error message             |
| `data.description` | string | Detailed description of the error |
| `data.path`        | string | Field path that caused the error  |

***

#### Testing

#### Test Scenarios

1. **Successful Validation**

* Use approved test card (4225289837059229)
* Verify `transaction_info.status: "Validated"`
* Verify `risk_info.status: "Approved"`
* Expected: Complete flow with void

2. **Fraud Decline**

* Use email: `john_autoreject@test.com`
* Verify `transaction_info.status: "Declined"`
* Verify `risk_info.status: "Denied"`
* Expected: Fails at fraud validation

3. **Invalid Card**

* Use invalid card number (e.g., 1234567890123456)
* Verify appropriate error response with `status: "fail"`
* Check `data.path: "card.number"`
* Expected: Request validation error

***


---

# Agent Instructions: 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:

```
GET https://firstoken.gitbook.io/api-docs/api-reference/payments/payments-api-koin/card-validation.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
