/api/transactions/transfer
GETTransfer coins between wallets with automatic change-making and coin breaking.
Description
The `/api/transactions/transfer` endpoint transfers a specified amount of CloudCoins from one wallet to another. The operation moves coin files directly between wallets (not copy-delete), preserving folder structure (Bank→Bank, Fracked→Fracked), and automatically makes change by breaking coins if exact denominations aren't available.
If exact change cannot be made with available coins, the system automatically breaks larger coins (up to 10 iterations) to create the denominations needed for the transfer. If change-making fails after maximum attempts, the transfer is aborted and an error is returned.
- Move coins between wallets for organization or security
- Distribute coins across multiple wallet locations
- Consolidate coins from multiple wallets into one
- Transfer specific amounts while maintaining exact change
- Automated wallet-to-wallet transfers in applications
- File Movement: Coin files are moved (not copied), ensuring they exist in only one location at a time.
- Folder Preservation: Coins in Bank folder stay in Bank; coins in Fracked folder stay in Fracked.
- No Authentication: Coins are transferred regardless of authentication status (Bank or Fracked).
- Exact Amount: The system ensures exactly the requested amount is transferred using automatic change-making.
- Transaction Logging: Both wallets receive transaction log entries with opposite symbols (withdraw/deposit).
Parameters
Query Parameters
Response
Returns a JSON object containing the transfer details and task ID for tracking.
Success Response Properties
Success Response Example
{
"command": "transaction-transfer",
"success": true,
"task_id": "Nov-13-25_09-24-05-PM-1537",
"from_wallet_path": "E:\\Data\\Wallets\\Default",
"to_wallet_path": "E:\\Data\\Wallets\\Savings",
"amount": 100,
"message": "Transfer completed successfully"
}
Error Responses
The endpoint returns different error codes depending on the failure reason.
HTTP 400 - Missing Parameters
{
"error": true,
"message": "Missing 'from_wallet_path' parameter",
"code": 400
}
HTTP 400 - Invalid Wallet Path
{
"error": true,
"message": "Invalid wallet path 'E:\\Data\\Wallets\\Invalid'. The wallet does not exist or is not accessible.",
"code": 400
}
HTTP 400 - Cannot Make Exact Change
{
"error": true,
"message": "Cannot make exact change after breaking coins. Please transfer whole notes.",
"code": 400
}
HTTP 400 - Insufficient Balance
{
"error": true,
"message": "Insufficient balance in source wallet",
"code": 400
}
HTTP 500 - File Move Failed
{
"error": true,
"message": "Failed to move coin files between wallets",
"code": 500
}
Examples
JavaScript (fetch)
const API_HOST = 'http://localhost:8080';
async function transferCoins(fromWalletPath, toWalletPath, amount) {
try {
const url = `${API_HOST}/api/transactions/transfer?` +
`from_wallet_path=${encodeURIComponent(fromWalletPath)}&` +
`to_wallet_path=${encodeURIComponent(toWalletPath)}&` +
`amount=${amount}`;
const response = await fetch(url);
const result = await response.json();
if (result.success) {
console.log(`✓ ${result.message}`);
console.log(`Task ID: ${result.task_id}`);
console.log(`From: ${result.from_wallet}`);
console.log(`To: ${result.to_wallet}`);
console.log(`Amount: ${result.amount} CloudCoins`);
return result.task_id;
} else {
console.error(`Error: ${result.message}`);
return null;
}
} catch (error) {
console.error('Request failed:', error);
return null;
}
}
// Transfer 100 CloudCoins from Default wallet to Savings wallet
transferCoins('E:\\Data\\Wallets\\Default', 'E:\\Data\\Wallets\\Savings', 100);
// Transfer with decimals (250.5 CloudCoins)
transferCoins('E:\\Data\\Wallets\\Default', 'E:\\Data\\Wallets\\Backup', 250.5);
cURL
# Transfer 100 CloudCoins from Default wallet to Savings wallet
curl -X GET "http://localhost:8080/api/transactions/transfer?from_wallet_path=E:\Data\Wallets\Default&to_wallet_path=E:\Data\Wallets\Savings&amount=100"
# Transfer with decimal amount (250.5 CloudCoins)
curl -X GET "http://localhost:8080/api/transactions/transfer?from_wallet_path=E:\Data\Wallets\Default&to_wallet_path=E:\Data\Wallets\Backup&amount=250.5"
# With verbose output to see headers
curl -v -X GET "http://localhost:8080/api/transactions/transfer?from_wallet_path=E:\Data\Wallets\Savings&to_wallet_path=E:\Data\Wallets\Default&amount=50"
# Pipe to jq for formatted JSON output
curl -s "http://localhost:8080/api/transactions/transfer?from_wallet_path=E:\Data\Wallets\Default&to_wallet_path=E:\Data\Wallets\Savings&amount=100" | jq .
Go
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
)
const ApiHost = "http://localhost:8080"
type TransferResponse struct {
Command string `json:"command"`
Success bool `json:"success"`
TaskID string `json:"task_id"`
FromWalletPath string `json:"from_wallet_path"`
ToWalletPath string `json:"to_wallet_path"`
Amount float64 `json:"amount"`
Message string `json:"message"`
}
type ErrorResponse struct {
Error bool `json:"error"`
Message string `json:"message"`
Code int `json:"code"`
}
func transferCoins(fromWalletPath, toWalletPath string, amount float64) (string, error) {
// Build URL with query parameters
params := url.Values{}
params.Add("from_wallet_path", fromWalletPath)
params.Add("to_wallet_path", toWalletPath)
params.Add("amount", fmt.Sprintf("%.2f", amount))
apiUrl := fmt.Sprintf("%s/api/transactions/transfer?%s", ApiHost, params.Encode())
resp, err := http.Get(apiUrl)
if err != nil {
return "", fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
// Check for success
if resp.StatusCode == http.StatusOK {
var result TransferResponse
if err := json.Unmarshal(body, &result); err != nil {
return "", err
}
fmt.Printf("✓ %s\n", result.Message)
fmt.Printf("Task ID: %s\n", result.TaskID)
fmt.Printf("From: %s\n", result.FromWalletPath)
fmt.Printf("To: %s\n", result.ToWalletPath)
fmt.Printf("Amount: %.2f CloudCoins\n", result.Amount)
return result.TaskID, nil
}
// Handle error response
var errResult ErrorResponse
if err := json.Unmarshal(body, &errResult); err != nil {
return "", err
}
return "", fmt.Errorf("error %d: %s", errResult.Code, errResult.Message)
}
func main() {
// Transfer 100 CloudCoins from Default wallet to Savings wallet
taskID, err := transferCoins("E:\\Data\\Wallets\\Default", "E:\\Data\\Wallets\\Savings", 100)
if err != nil {
fmt.Printf("Transfer failed: %v\n", err)
return
}
fmt.Printf("\nTransfer completed successfully!\n")
fmt.Printf("Track this transfer using Task ID: %s\n", taskID)
}
Implementation Details
Understanding how the transfer operation works internally:
- Parameter Validation: Validates that all required parameters (from_wallet_path, to_wallet_path, amount) are provided.
- Wallet Path Validation: Ensures both wallet paths exist and are accessible.
- Balance Check: Scans source wallet to verify sufficient balance for the transfer.
- Automatic Change-Making: Uses greedy algorithm to find exact change from available coins.
- Coin Breaking: If exact change cannot be made, automatically breaks larger coins (up to 10 iterations) to create needed denominations.
- File Movement: Moves selected coin files from source to destination using rename() operation (atomic on same filesystem).
- Folder Preservation: Coins in Bank folder are moved to destination's Bank; Fracked coins go to Fracked.
- Transaction Logging: Logs withdrawal in source wallet's transactions.csv and deposit in destination wallet's transactions.csv.
- Task ID Generation: Creates unique task ID (format: "MMM-DD-YY_HH-MM-SS-AM/PM-XXXX") for tracking.
The transfer uses a greedy algorithm (largest coins first) to make exact change. If that fails, it automatically breaks the smallest coin larger than the remaining needed amount, then retries. This process repeats up to 10 times to ensure exact change can be made. This prevents over-sending and ensures precise transfers.
Both wallets receive entries in their transactions.csv file:
- Source wallet: "◀" symbol (Transfer Out) with amount withdrawn
- Destination wallet: "▶" symbol (Transfer In) with amount deposited
- Both entries share the same task_id for correlation