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:
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.
Review Transfer Details
Retrieve the failed transfer by its ID to inspect associated orders, bags, and error messages
Use the Get Transfer by ID or Get Transfer by Payment Provider ID API
Diagnose the Root Cause
Analyze the failure by examining the
errors
array in the transfer object for specific error codes and messagesCommon issues include missing payout accounts, incomplete verification, or Stripe balance issues
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
Retry the Transfer
Use the appropriate API to retry the transfer:
Monitor for Completion
Confirm that the transfer now shows
status=SENT
via the APIOptionally subscribe to the
TRANSFER_SENT
webhook to track successful transfers
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
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.
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:
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
andTRANSFER_SENT
webhooksMonitor for spikes in
FAILED
orPENDING
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 aPENDING
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?