# Handling Failed Transfers

Transfer failures can occur for a variety of reasons—ranging from missing payout accounts to external payment provider restrictions. This guide walks through how to identify failed transfers, understand the root causes, and retry them using Violet's APIs.

### Steps to Handle a Failed Transfer

Follow these steps to identify and resolve failed transfers:

1. **Identify Failed Transfers**

   * Use the [Search Transfers API](https://docs.violet.io/api-reference/payments/transfers/search-transfers) to find transfers with `status=FAILED`
   * Optionally listen to `TRANSFER_FAILED` webhook events for real-time failure notifications

   The detailed filtering examples and response structures are covered in the "Identifying Failed Transfers" section below.
2. **Review Transfer Details**
   * Retrieve the failed transfer by its ID to inspect associated orders, bags, and error messages
   * Use the [Get Transfer by ID](https://docs.violet.io/api-reference/payments/transfers/get-transfer) or [Get Transfer by Payment Provider ID](https://docs.violet.io/api-reference/payments/transfers/get-transfer-by-provider-id) API
3. **Diagnose the Root Cause**
   * Analyze the failure by examining the `errors` array in the transfer object for specific error codes and messages
   * Common issues include missing payout accounts, incomplete verification, or Stripe balance issues
4. **Resolve the Underlying Issue**
   * Help merchants complete onboarding, verification, or compliance steps
   * Ensure your Stripe platform account has sufficient funds if you're managing float
5. **Retry the Transfer**
   * Use the appropriate API to retry the transfer:
     * [Single Order](https://docs.violet.io/api-reference/order-service/transfers/transfer-funds-single-order)
     * [Single Bag](https://docs.violet.io/api-reference/order-service/transfers/transfer-funds-single-bag)
     * [Multiple Orders](https://docs.violet.io/api-reference/order-service/transfers/transfer-funds-multiple-orders)
     * [Multiple Bags](https://docs.violet.io/api-reference/order-service/transfers/transfer-funds-multiple-bags)
6. **Monitor for Completion**
   * Confirm that the transfer now shows `status=SENT` via the API
   * Optionally subscribe to the `TRANSFER_SENT` webhook to track successful transfers
7. **Escalate if Needed**
   * If the issue persists across retries, or data appears inconsistent, contact Violet support for assistance

Details for each of these steps are included further in this guide.

### Overview of Transfers

Each **Transfer** object represents a movement of funds from the platform to a merchant. A failed transfer means a merchant has not received funds, but customer payment and order placement have completed successfully.

#### Key Fields in the Transfer Object

| Field                          | Type             | Description                                                                                                                              |
| ------------------------------ | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `merchant_id`                  | Integer          | Merchant receiving the funds                                                                                                             |
| `payment_provider_transfer_id` | String           | Transfer ID from Stripe or another provider. *The `payment_provider_transfer_id` field will only be present once the Transfer succeeds.* |
| `status`                       | String           | Status of the transfer                                                                                                                   |
| `amount`                       | Integer          | Amount in cents or smallest currency unit                                                                                                |
| `currency`                     | String           | e.g. `"USD"`                                                                                                                             |
| `related_bags`                 | Array of Strings | Bag IDs associated with this transfer                                                                                                    |
| `related_orders`               | Array of Strings | Order IDs associated with this transfer                                                                                                  |
| `related_distributions`        | Array of Strings | Distribution records funded by this transfer                                                                                             |
| `errors`                       | Array            | Error details if status is `FAILED`                                                                                                      |

#### Sample Transfer Object

```json
{
  "id": 335500,
  "merchant_id": 12345,
  "status": "FAILED",
  "amount": 10000,
  "currency": "USD",
  "related_bags": ["12345", "12346"],
  "related_orders": ["22345"],
  "related_distributions": ["1118941"],
  "errors": [
    {
      "code": "insufficient_funds",
      "message": "Insufficient funds in platform account"
    }
  ]
}
```

### Transfer States

The `status` field on the Transfer object can be one of the following values:

> Use these statuses to monitor transfer health and recovery progress.

| Status               | Description                                                                                                      |
| -------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `PENDING`            | Transfer has been created but not yet sent to the payment provider                                               |
| `SENT`               | Transfer has been submitted successfully to the payment provider                                                 |
| `FAILED`             | Transfer failed during submission or processing                                                                  |
| `PARTIALLY_SENT`     | Some but not all distributions in a multi-merchant order were successfully transferred                           |
| `REVERSED`           | Transfer was successfully submitted but later reversed in full                                                   |
| `PARTIALLY_REVERSED` | Only part of the originally sent transfer amount was reversed                                                    |
| `BYPASSED`           | Transfer was intentionally skipped or not required (typically due to config like DISABLED or EXTERNAL transfers) |

> Use these statuses to monitor transfer health and recovery progress.

### Identifying Failed Transfers

#### 1. [Search Transfers API](https://docs.violet.io/api-reference/payments/transfers/search-transfers)

To retrieve all failed transfers, use the Search Transfers API with the status parameter set to `FAILED`:

```bash
curl --request POST \
  --url https://sandbox-api.violet.io/v1/payments/transfers \
  --header 'Content-Type: application/json' \
  --header 'X-Violet-App-Id: <X-Violet-App-ID>' \
  --header 'X-Violet-App-Secret: <X-Violet-App-Secret>' \
  --header 'X-Violet-Token: <X-Violet-Token>' \
  --data '{
    "status": "FAILED"
  }'
```

**Filtering by Merchant**

To focus on failed transfers for a specific merchant, combine the status filter with merchant\_id:

```bash
curl --request POST \
  --url https://sandbox-api.violet.io/v1/payments/transfers \
  --header 'Content-Type: application/json' \
  --header 'X-Violet-App-Id: <X-Violet-App-ID>' \
  --header 'X-Violet-App-Secret: <X-Violet-App-Secret>' \
  --header 'X-Violet-Token: <X-Violet-Token>' \
  --data '{
    "merchant_id": 12345,
    "status": "FAILED"
  }'
```

**Filtering by Date Range**

To analyze failed transfers within a specific timeframe, use the date range parameters:

```bash
curl --request POST \
  --url https://sandbox-api.violet.io/v1/payments/transfers \
  --header 'Content-Type: application/json' \
  --header 'X-Violet-App-Id: <X-Violet-App-ID>' \
  --header 'X-Violet-App-Secret: <X-Violet-App-Secret>' \
  --header 'X-Violet-Token: <X-Violet-Token>' \
  --data '{
    "status": "FAILED",
    "created_after": "2025-07-05T00:00:00Z",
    "created_before": "2025-07-12T23:59:59Z"
  }'
```

#### Understanding Failed Transfer Response

The Search API will return a Page of `FAILED` transfers with the queries above. Each Transfer will be present in the `content` array of the response, and will contain details about the errors that caused the failure.

```json
{
  "content": [
    {
      "id": 123,
      "status": "FAILED",
      "amount": 10000,
      "currency": "USD",
      "merchant_id": 12345,
      "payout_account_id": 789,
      "payment_provider": "STRIPE",
      "errors": [
        {
          "payout_transfer_id": 123,
          "error_code": 1001,
          "error_message": "Insufficient funds in source account",
          "date_created": "2023-11-07T05:31:56Z"
        }
      ],
      "date_created": "2023-11-07T05:31:56Z",
      "date_last_modified": "2023-11-07T05:31:56Z"
    }
  ],
  "pageable": {
    "page_number": 0,
    "page_size": 20,
    "sort": {
      "sorted": true,
      "unsorted": false,
      "empty": false
    },
    "offset": 0,
    "paged": true,
    "unpaged": false
  },
  "total_pages": 1,
  "total_elements": 1,
  "last": true,
  "number_of_elements": 1,
  "first": true,
  "size": 20,
  "number": 0,
  "sort": {
    "sorted": true,
    "unsorted": false,
    "empty": false
  },
  "empty": false
}
```

#### 2. TRANSFER\_FAILED Webhook

Violet provides a dedicated `FAILED_TRANSFER` webhook event that notifies you when a transfer fails. This event includes detailed information about the transfer and the associated errors, allowing you to take immediate action.

Listen for this webhook to receive real-time alerts:

```json
{
  "event": "TRANSFER_FAILED",
  "data": {
    "transfer_id": 335500,
    "merchant_id": 1234,
    "amount": 11428,
    "currency": "USD",
    "errors": [
      {
        "code": "insufficient_funds",
        "message": "Insufficient funds in platform account"
      }
    ]
  }
}
```

#### 3. [Get Pending Transfers API](https://docs.violet.io/api-reference/payments/transfers/get-pending-transfers)

To retrieve all pending transfers for a specific merchant, you can use the Get Pending Transfers API. This is useful for identifying which transfers are awaiting resolution or retry.

```
GET /payments/transfers/pending?merchant_id=12345
```

***

### Common Failure Scenarios

Failed transfers typically fall into several categories. The most common failures are:

| Cause                       | Definition                                                  |
| --------------------------- | ----------------------------------------------------------- |
| Missing Payout Account      | Merchant never connected a payout method                    |
| Account Verification Issues | Stripe or banking KYC not completed                         |
| KYC Verification Pending    | Know Your Customer verification is still in progress        |
| Regulatory Restrictions     | Legal or compliance flags in destination country            |
| Insufficient Platform Funds | Platform account lacks sufficient balance for transfer      |
| Payment Provider Errors     | Stripe returned an error like `balance_insufficient`        |
| System-Level Errors         | Database or async workflow issues during transfer execution |

When transfers fail, the associated `Distribution` objects remain in the `PENDING` state, allowing you to retry the transfer once the issues are resolved.

***

### Recovery Options

#### 1. Automatic Retries

If you’re noticing a `FAILED` Transfer, this means Violet has already extinguished its automatic retry strategy, and some manual action is needed for these Transfers to succeed. You can use the Manual Transfer Retry strategy to manually reconcile these transfers once the underlying issue has been resolved.

#### 2. Manual Transfer Retry

Violet provides multiple APIs to help you settle Transfers, whether at the individual Bag level, individual Order level, or in bulk across multiple Bags or Orders.

When settling across **multiple Bags or Orders**, Violet aggregates all pending Distributions for each merchant and initiates **a single Transfer per merchant**. For example, if a merchant has pending funds across 4 Bags, using the bulk settlement API will result in **one consolidated Transfer**, not four.

#### Retry by Order:

[Transfer Funds for a Single Order API](https://docs.violet.io/api-reference/order-service/transfers/transfer-funds-single-order)

```
POST /order-service/transfers/order/{order_id}
```

#### Retry by Bag:

[Transfer Funds for a Single Bag API](https://docs.violet.io/api-reference/order-service/transfers/transfer-funds-single-bag)

```
POST /order-service/transfers/bag/{bag_id}
```

#### Retry by Multiple Orders:

[Transfer Funds for Multiple Orders API](https://docs.violet.io/api-reference/order-service/transfers/transfer-funds-multiple-orders)

```
POST /order-service/transfers/orders
```

#### Retry by Multiple Bags:

[Transfer Funds for Multiple Bags API](https://docs.violet.io/api-reference/order-service/transfers/transfer-funds-multiple-bags)

```
POST /order-service/transfers/bags
```

> Before retrying, make sure the root cause has been resolved (e.g., payout account added, verification completed, etc.).

***

### Best Practices

#### Monitoring & Alerts

* Subscribe to `TRANSFER_FAILED` and `TRANSFER_SENT` webhooks
  * Monitor for spikes in `FAILED` or `PENDING` statuses via these webhooks, or use the Search Transfers API.
  * Alternatively, periodically use the Get Order by ID API to fetch details about the Order, including Payment Transaction. When a Transfer has not been processed for a given Order, the Payment Transaction `transfer_status` will also be in a `PENDING` state.

#### Communication with Merchants

* Notify merchants when their transfers fail due to set up issues, such as missing Payout Accounts or missing KYC in their Stripe account.
* Include clear instructions on next steps (e.g., connect payout account, verify identity)

***

### Advanced Scenarios

#### Partial Failures

Multi-merchant orders may have **some** successful and **some** failed transfers. Use the `Search Transfers` API to isolate only the failed ones and retry them individually.

### When to Contact Violet Support

Reach out to Violet if:

* Transfers are consistently failing for specific merchants
* Transfer states look inconsistent across systems
* Distributions data for a Transfer does not line up with what you’re seeing in your Stripe account.
