# Create transaction

> Creates a single sales transaction. To create several transactions at once, see the Batch upload page.

Página: https://doc.rapidchargeback.com/en/canais/transactions/criar-transacao/

Creates a single sales transaction. To create several transactions at once, see the [Batch upload](https://doc.rapidchargeback.com/en/canais/transactions/envio-em-lote/) page.

## Endpoint

```
POST https://api.rapidchargeback.com/api/v1/transactions
```

## Authentication

```
Authorization: Basic base64(client_id:client_secret)
Content-Type: application/json
```

---

## Required fields

| Field | Type | Description |
|---|---|---|
| `merchant_id` **or** `merchant_ref` | string | Send **exactly one**: `merchant_id` (the merchant's UUID at Rapid, must belong to your company) **or** `merchant_ref` (the seller's ID in **your** system, up to 100 characters, for channels/platforms with several sellers). If `merchant_ref` is unknown, the merchant is **created** automatically with `merchant_name` (optional, up to 255 characters; ignored when the merchant already exists). Sending both → `422 VALIDATION_ERROR`. Your merchants and their `merchant_id` are in [List merchants](https://doc.rapidchargeback.com/en/canais/merchants/listar-merchants/). |
| `external_id` | string | Transaction ID in the source system |
| `amount` | number | Transaction amount (must be positive) |
| `currency` | string | ISO 4217 code with 3 uppercase letters (e.g. `USD`, `BRL`) |
| `transaction_date` | string | Transaction date in ISO 8601 format (e.g. `2026-04-01T15:30:00Z`) |
| `card_last4` | string | Last 4 digits of the card (exactly 4 numeric digits) |
| `items` | array | At least 1 item (see the item fields below) |

## Item fields

### Required

| Field | Type | Description |
|---|---|---|
| `product_description` | string | Product description (up to 1000 characters; above that the request fails) |
| `unit_price` | number | Unit price |

### Optional

| Field | Type | Description |
|---|---|---|
| `product_name` | string | Product name |
| `quantity` | number | Quantity |
| `unit_of_measure` | string | Unit of measure |
| `category` | string | Product category |

---

## Optional fields

| Field | Type | Description |
|---|---|---|
| `external_source` | string | Source system (default: `custom`) |
| `order_number` | string | Order number |
| `transaction_id` | string | Transaction ID in the payment gateway. See the note below |
| `arn` | string | Acquirer Reference Number |
| `banknet_ref` | string | Banknet reference (Mastercard) |
| `auth_code` | string | Authorization code |
| `network` | string | Card network: `visa`, `mastercard`, `amex`, `discover`, `other` |
| `card_bin` | string | Card BIN (6 to 8 numeric digits) |
| `ip_address` | string | Buyer's IP address |
| `clearing_datetime` | string | Clearing date (ISO 8601) |
| `mcc` | string | Merchant Category Code |
| `eci` | string | Electronic Commerce Indicator |
| `pos_entry_mode` | string | POS entry mode |
| `descriptor` | string | Billing descriptor |
| `correlation_id` | string | Correlation ID |
| `device_id` | string | Device ID (min. 15 characters) |
| `device_fingerprint` | string | Device fingerprint (min. 20 characters) |
| `customer` | object | Buyer data (see below) |
| `addresses` | array | Shipping and/or billing addresses (see below) |
| `payments` | array | Payment data (see below) |
| `refunds` | array | Refund data (see below) |

### Size limits

Every text field has a ceiling. Above it the response is `422 VALIDATION_ERROR` naming the field, never a server error.

| Limit | Fields |
|---|---|
| 1000 | `product_description` |
| 255 | `descriptor`, `merchant_name`, `product_name`, `device_fingerprint`, `street`, `reason` (refund) |
| 200 | `billing_name` |
| 100 | `external_id`, `order_number`, `transaction_id`, `arn`, `banknet_ref`, `correlation_id`, `merchant_ref`, `device_id`, `first_name`, `last_name`, `city`, `state`, `category` |
| 64 | `method_masked`, `reference_number` |
| 45 | `ip_address` (fits IPv6) |
| 50 | `external_source`, `auth_code`, `account_id` |
| 32 | `unit_of_measure` |
| 30 | `phone`, `postal_code`, `wallet_indicator` |
| 20 | `number` (address) |
| 16 | `payment_type` |
| 10 | `mcc`, `pos_entry_mode` |
| 7 | `card_expiry` |
| 5 | `eci` |
| 2 | `cvv2_presence_indicator`, `avs_result` |

`email` follows the 255 limit and must have a valid format. `currency`, `card_bin`, `card_last4`, `country` and `network` have a fixed format, described in the fields table.

### Note on `transaction_id`

It is optional, but fill it in whenever the charge went through a payment gateway: this is the field Rapid uses to link a future chargeback to the transaction. Send the charge identifier in the gateway, not your order number (that one is `order_number`).

If you use a gateway whose disputes reach Rapid and the field is empty, the chargeback is still received, but with no associated transaction: no history, no attached evidence and no authentication data, which is exactly the defense material. An equivalent alternative: send `external_source` with the gateway name and `external_id` with the charge id.

---

## `customer` object (all optional)

| Field | Type | Description |
|---|---|---|
| `first_name` | string | First name |
| `last_name` | string | Last name |
| `email` | string | Email (valid format) |
| `billing_name` | string | Name on the card |
| `account_id` | string | Buyer's account ID in your system |
| `phone` | string | Phone |

## `address` object (inside the `addresses` array)

| Field | Type | Required | Description |
|---|---|---|---|
| `type` | string | Yes | `shipping` or `billing` |
| `street` | string | No | Street |
| `number` | string | No | Number |
| `city` | string | No | City |
| `state` | string | No | State/Region |
| `postal_code` | string | No | Postal code |
| `country` | string | No | ISO 3166-1 country code, 2 or 3 uppercase letters (`BR`/`BRA`, `US`/`USA`). Alpha-3 recommended. |

## Payment authentication fields (root, all optional)

Inputs for [fraud protection](https://doc.rapidchargeback.com/en/produtos/prevencao/integracao/#for-fraud-protection) and 3-DS. The more you fill in, the stronger the deflection and the defense.

| Field | Type | Description |
|---|---|---|
| `cvv2_presence_indicator` | string | CVV2 presence indicator in the authorization |
| `cvv2_result` | string | CVV2 check result: `M`, `N`, `P`, `S`, `U` or `Y`, as it came in the authorization response |
| `avs_result` | string | AVS result (address verification) |
| `cardholder_verification_approved` | boolean | 3-DS/cardholder verification approved |
| `authentication_response_type` | string | 3-DS authentication response type: `attempt` (attempt) or `confirm` (authentication confirmed) |
| `wallet_indicator` | string | Digital wallet used (e.g. Apple Pay, Google Pay) |

### Buyer account (inside `customer`, all optional)

They say whether the purchase was made by a known account, and since when it exists. They are the strongest defense material in a fraud dispute: a buyer with an old account who was logged in is hard to dispute as "I do not recognize it".

| Field | Type | Description |
|---|---|---|
| `registered_at` | string (ISO 8601) | When the buyer's account was created in your system |
| `registered_at_source` | string | Where `registered_at` comes from: `merchant_api` (account record in your system) or `client_declared` (your statement, without that record) |
| `account_authenticated` | boolean | The buyer was logged into the account when purchasing |
| `account_authenticated_source` | string | How you know it: `per_account` (verified in this purchase) or `onboarding_flag` (your store's rule, for example, every purchase requires login) |
| `account_authenticated_at` | string (ISO 8601) | When the login happened |
| `auth_method` | string | How the buyer logged in: `password`, `otp`, `2fa`, `biometric`, `oauth` or `other` |
| `account_source_method` | string | Where the account data comes from: `merchant_api` or `client_declared` |
| `checkout_type` | string | How the purchase was made: `guest` (no account), `authenticated` (existing account, logged in) or `registered_at_checkout` (account created during the purchase) |

A value outside the lists responds `422 VALIDATION_ERROR`.

## `payment` object (all optional)

| Field | Type | Description |
|---|---|---|
| `payment_type` | string | Payment type (credit, debit, pix, etc.) |
| `method_masked` | string | Masked number |
| `matched_payment` | boolean | Whether this payment was the one used (default: true) |
| `card_bin` | string | BIN (6-8 digits) |
| `card_last4` | string | Last 4 digits (4 numeric digits) |
| `installments` | integer | Number of installments |
| `card_expiry` | string | Expiry (MM/YYYY) |

## `refund` object

| Field | Type | Required | Description |
|---|---|---|---|
| `amount` | number | Yes | Refund amount (positive) |
| `currency` | string | Yes | Currency (3 uppercase letters, ISO 4217) |
| `reference_number` | string | No | Refund identifier |
| `reason` | string | No | Refund reason |
| `refund_datetime` | string | No | Refund date (ISO 8601) |

---

## Validations

- `transaction_date` must be in ISO 8601 with timezone
- `currency` must be an ISO 4217 code (3 uppercase letters)
- `amount` must be greater than zero
- `card_last4` must have exactly 4 numeric digits
- `card_bin` must have 6 to 8 numeric digits
- `device_id` must have at least 15 characters
- `device_fingerprint` must have at least 20 characters
- The `external_source + external_id` combination cannot duplicate an existing transaction of your company

## Warnings

The API returns notices (without blocking creation) when important optional fields are missing:

| When | Notice |
|---|---|
| No `card_bin` | `card_bin missing: reduces match accuracy` |
| No `order_number` | `order_number missing: recommended to identify the order in dispute responses` |
| No `ip_address`, `device_id` **and** `device_fingerprint` | `ip_address, device_id and device_fingerprint missing: recommended, fraud protection relies on them` |

**One** of the three identifiers is enough to clear the last notice: the IP is not required. And there is no address notice on purpose: whoever sells digital goods should not send a shipping address (see [fraud protection](https://doc.rapidchargeback.com/en/produtos/prevencao/integracao/#digital-goods-do-not-send-a-shipping-address)).

The `warnings` key **only shows up** in the response when there is at least one notice. Do not count on `warnings: []`.

---

## Request example

The credentials come from environment variables (`RAPID_CLIENT_ID` and `RAPID_CLIENT_SECRET`), never written in the code.

**cURL**

```bash
curl -X POST https://api.rapidchargeback.com/api/v1/transactions \
  -H "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_id": "00000000-0000-0000-0000-000000000001",
    "external_source": "shopify",
    "external_id": "TXN-2026-001",
    "transaction_date": "2026-04-01T15:30:00Z",
    "amount": 150.00,
    "currency": "USD",
    "card_last4": "4242",
    "card_bin": "424242",
    "order_number": "ORD-001",
    "auth_code": "AUTH999",
    "network": "visa",
    "descriptor": "LOJA EXEMPLO",
    "ip_address": "203.0.113.10",
    "items": [
      {
        "product_description": "Assinatura Premium - Mensal",
        "product_name": "Plano Premium",
        "quantity": 1,
        "unit_price": 150.00
      }
    ],
    "customer": {
      "first_name": "João",
      "last_name": "Silva",
      "email": "joao@exemplo.com"
    },
    "addresses": [
      {
        "type": "shipping",
        "street": "Rua das Flores",
        "number": "123",
        "city": "São Paulo",
        "state": "SP",
        "postal_code": "01001000",
        "country": "BRA"
      }
    ]
  }'
```

**Node.js**

```javascript
const credentials = Buffer.from(`${process.env.RAPID_CLIENT_ID}:${process.env.RAPID_CLIENT_SECRET}`).toString('base64')

const response = await fetch('https://api.rapidchargeback.com/api/v1/transactions', {
  method: 'POST',
  headers: { Authorization: `Basic ${credentials}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    merchant_id: '00000000-0000-0000-0000-000000000001',
    external_source: 'shopify',
    external_id: 'TXN-2026-001',
    transaction_date: '2026-04-01T15:30:00Z',
    amount: 150.0,
    currency: 'USD',
    card_last4: '4242',
    card_bin: '424242',
    order_number: 'ORD-001',
    auth_code: 'AUTH999',
    network: 'visa',
    descriptor: 'LOJA EXEMPLO',
    ip_address: '203.0.113.10',
    items: [
      {
        product_description: 'Assinatura Premium - Mensal',
        product_name: 'Plano Premium',
        quantity: 1,
        unit_price: 150.0,
      },
    ],
    customer: {
      first_name: 'João',
      last_name: 'Silva',
      email: 'joao@exemplo.com',
    },
    addresses: [
      {
        type: 'shipping',
        street: 'Rua das Flores',
        number: '123',
        city: 'São Paulo',
        state: 'SP',
        postal_code: '01001000',
        country: 'BRA',
      },
    ],
  }),
})

const body = await response.json()
if (!response.ok) throw new Error(`${response.status} ${body.error.code}: ${body.error.message}`)

console.log('Transaction created:', body.data.id)
for (const warning of body.warnings ?? []) console.warn('Warning:', warning)
```

**Python**

```python
import os

import requests

response = requests.post(
    "https://api.rapidchargeback.com/api/v1/transactions",
    auth=(os.environ["RAPID_CLIENT_ID"], os.environ["RAPID_CLIENT_SECRET"]),
    json={
        "merchant_id": "00000000-0000-0000-0000-000000000001",
        "external_source": "shopify",
        "external_id": "TXN-2026-001",
        "transaction_date": "2026-04-01T15:30:00Z",
        "amount": 150.00,
        "currency": "USD",
        "card_last4": "4242",
        "card_bin": "424242",
        "order_number": "ORD-001",
        "auth_code": "AUTH999",
        "network": "visa",
        "descriptor": "LOJA EXEMPLO",
        "ip_address": "203.0.113.10",
        "items": [
            {
                "product_description": "Assinatura Premium - Mensal",
                "product_name": "Plano Premium",
                "quantity": 1,
                "unit_price": 150.00,
            }
        ],
        "customer": {
            "first_name": "João",
            "last_name": "Silva",
            "email": "joao@exemplo.com",
        },
        "addresses": [
            {
                "type": "shipping",
                "street": "Rua das Flores",
                "number": "123",
                "city": "São Paulo",
                "state": "SP",
                "postal_code": "01001000",
                "country": "BRA",
            }
        ],
    },
    timeout=30,
)

body = response.json()
if not response.ok:
    raise RuntimeError(f"{response.status_code} {body['error']['code']}: {body['error']['message']}")

print("Transaction created:", body["data"]["id"])
for warning in body.get("warnings", []):
    print("Warning:", warning)
```

**PHP**

```php
<?php
$transaction = [
    'merchant_id' => '00000000-0000-0000-0000-000000000001',
    'external_source' => 'shopify',
    'external_id' => 'TXN-2026-001',
    'transaction_date' => '2026-04-01T15:30:00Z',
    'amount' => 150.00,
    'currency' => 'USD',
    'card_last4' => '4242',
    'card_bin' => '424242',
    'order_number' => 'ORD-001',
    'auth_code' => 'AUTH999',
    'network' => 'visa',
    'descriptor' => 'LOJA EXEMPLO',
    'ip_address' => '203.0.113.10',
    'items' => [
        [
            'product_description' => 'Assinatura Premium - Mensal',
            'product_name' => 'Plano Premium',
            'quantity' => 1,
            'unit_price' => 150.00,
        ],
    ],
    'customer' => [
        'first_name' => 'João',
        'last_name' => 'Silva',
        'email' => 'joao@exemplo.com',
    ],
    'addresses' => [
        [
            'type' => 'shipping',
            'street' => 'Rua das Flores',
            'number' => '123',
            'city' => 'São Paulo',
            'state' => 'SP',
            'postal_code' => '01001000',
            'country' => 'BRA',
        ],
    ],
];

$ch = curl_init('https://api.rapidchargeback.com/api/v1/transactions');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_USERPWD => getenv('RAPID_CLIENT_ID') . ':' . getenv('RAPID_CLIENT_SECRET'),
    CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
    CURLOPT_POSTFIELDS => json_encode($transaction),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
]);

$raw = curl_exec($ch);
if ($raw === false) {
    throw new RuntimeException('Network error: ' . curl_error($ch));
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$body = json_decode($raw, true);

if ($status >= 400) {
    throw new RuntimeException("$status {$body['error']['code']}: {$body['error']['message']}");
}

echo 'Transaction created: ', $body['data']['id'], PHP_EOL;
foreach ($body['warnings'] ?? [] as $warning) {
    echo 'Warning: ', $warning, PHP_EOL;
}
```

---

## Response examples

### Success

```json
{
  "data": {
    "id": "00000000-0000-0000-0000-000000000042",
    "company_id": "...",
    "merchant_id": "00000000-0000-0000-0000-000000000001",
    "external_id": "TXN-2026-001",
    "amount": 150.00,
    "currency": "USD",
    "transaction_date": "2026-04-01T15:30:00.000Z",
    "card_last4": "4242",
    "created_at": "2026-04-01T15:31:00.000Z",
    "transaction_items": [...],
    "transaction_customers": [...],
    "transaction_addresses": [...]
  }
}
```

### Duplicate

```json
{
  "error": {
    "code": "TRANSACTION_DUPLICATE",
    "message": "Transaction already exists"
  }
}
```

### Validation error

```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "card_last4 must be exactly 4 digits"
  }
}
```
