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 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

  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

  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

{
  "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

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

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:

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:

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.

{
  "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:

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

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

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

Retry by Bag:

Transfer Funds for a Single Bag API

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

Retry by Multiple Orders:

Transfer Funds for Multiple Orders API

POST /order-service/transfers/orders

Retry by Multiple Bags:

Transfer Funds for Multiple Bags API

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.

Last updated

Was this helpful?