Bridge Transfers Orchestration
Quick Reference
typescript
type PaymentRail = | 'ach_push' | 'ach' | 'wire' // Fiat US | 'sepa' // Fiat EU | 'spei' // Fiat Mexico | 'ethereum' | 'solana' | 'polygon' | 'base' | 'arbitrum' | 'tron' // Crypto | 'bridge_wallet'; // Bridge wallets type TransferState = 'awaiting_funds' | 'completed' | 'failed' | 'pending'; type Currency = 'usd' | 'eur' | 'mxn' | 'usdc' | 'usdb';
Create Transfer (Onramp - Fiat to Crypto)
typescript
interface CreateTransferRequest {
amount: string;
on_behalf_of: string;
developer_fee?: string;
developer_fee_percent?: string;
source: {
payment_rail: PaymentRail;
currency: Currency;
from_address?: string; // For crypto sources
};
destination: {
payment_rail: PaymentRail;
currency: Currency;
to_address: string; // Required for crypto destinations
external_account_id?: string; // For fiat destinations
};
features?: {
flexible_amount?: boolean; // Allow any deposit amount
};
}
interface Transfer {
id: string;
client_reference_id?: string;
state: TransferState;
on_behalf_of: string;
amount: string;
developer_fee: string;
source: {
payment_rail: PaymentRail;
currency: Currency;
from_address?: string;
};
destination: {
payment_rail: PaymentRail;
currency: Currency;
to_address?: string;
external_account_id?: string;
};
source_deposit_instructions?: {
payment_rail: PaymentRail;
currency: Currency;
amount?: string;
from_address?: string;
to_address?: string;
bank_account_number?: string;
bank_routing_number?: string;
bank_beneficiary_name?: string;
deposit_message?: string;
iban?: string;
bic?: string;
};
receipt?: {
initial_amount: string;
developer_fee: string;
exchange_fee: string;
subtotal_amount: string;
gas_fee: string;
final_amount: string;
destination_tx_hash?: string;
url?: string;
};
created_at: string;
updated_at: string;
}
async function createTransfer(request: CreateTransferRequest): Promise<Transfer> {
const response = await fetch(`${BRIDGE_API_URL}/transfers`, {
method: 'POST',
headers: {
'Api-Key': API_KEY,
'Idempotency-Key': crypto.randomUUID(),
'Content-Type': 'application/json',
},
body: JSON.stringify(request),
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Transfer creation failed: ${error.message}`);
}
return response.json();
}
USD ACH to USDC (Ethereum)
typescript
async function onrampUSDToCrypto(
customerId: string,
amount: string,
cryptoAddress: string
): Promise<Transfer> {
return createTransfer({
amount,
on_behalf_of: customerId,
source: {
payment_rail: 'ach_push',
currency: 'usd',
},
destination: {
payment_rail: 'ethereum',
currency: 'usdc',
to_address: cryptoAddress,
},
});
}
// Usage
const transfer = await onrampUSDToCrypto(
'cust_alice',
'1000.00',
'0xdeadbeef'
);
console.log('Transfer ID:', transfer.id);
console.log('Status:', transfer.state);
console.log('Deposit Instructions:', transfer.source_deposit_instructions);
EUR SEPA to USDC (Solana)
typescript
async function onrampEURToCrypto(
customerId: string,
amount: string,
solanaAddress: string
): Promise<Transfer> {
return createTransfer({
amount,
on_behalf_of: customerId,
source: {
payment_rail: 'sepa',
currency: 'eur',
},
destination: {
payment_rail: 'solana',
currency: 'usdc',
to_address: solanaAddress,
},
});
}
Flexible Amount Onramp
typescript
async function flexibleOnramp(
customerId: string,
cryptoAddress: string,
feePercent: string = '1.0'
): Promise<Transfer> {
return createTransfer({
on_behalf_of: customerId,
source: {
payment_rail: 'ach_push',
currency: 'usd',
},
destination: {
payment_rail: 'ethereum',
currency: 'usdc',
to_address: cryptoAddress,
},
developer_fee_percent: feePercent,
features: {
flexible_amount: true, // Customer can send any amount
},
});
}
Offramp (Crypto to Fiat)
typescript
async function offrampCryptoToUSD(
customerId: string,
amount: string,
externalAccountId: string,
cryptoAddress: string
): Promise<Transfer> {
return createTransfer({
amount,
on_behalf_of: customerId,
source: {
payment_rail: 'ethereum',
currency: 'usdc',
from_address: cryptoAddress,
},
destination: {
payment_rail: 'ach',
currency: 'usd',
external_account_id: externalAccountId,
},
});
}
// Offrump with Tron USDT
async function offrampUSDTFromTron(
customerId: string,
amount: string,
externalAccountId: string,
tronAddress: string,
blockchainMemo?: string
): Promise<Transfer> {
return createTransfer({
amount,
on_behalf_of: customerId,
source: {
payment_rail: 'tron',
currency: 'usdt',
from_address: tronAddress,
},
destination: {
payment_rail: 'ach',
currency: 'usd',
external_account_id: externalAccountId,
},
});
}
Wallet-to-Wallet Transfer
typescript
async function walletToWalletTransfer(
customerId: string,
amount: string,
fromWalletId: string,
toWalletId: string,
chain: PaymentRail = 'solana'
): Promise<Transfer> {
return createTransfer({
amount,
on_behalf_of: customerId,
source: {
payment_rail: 'bridge_wallet',
currency: 'usdc',
from_address: fromWalletId, // Actually bridge_wallet_id
},
destination: {
payment_rail: chain,
currency: 'usdc',
to_address: toWalletId,
},
});
}
// Multi-recipient payroll
async function processPayroll(
employerCustomerId: string,
employerWalletId: string,
payments: { walletId: string; chain: PaymentRail; amount: string }[]
): Promise<Transfer[]> {
const results: Transfer[] = [];
for (const payment of payments) {
const transfer = await walletToWalletTransfer(
employerCustomerId,
payment.amount,
employerWalletId,
payment.walletId,
payment.chain
);
results.push(transfer);
}
return results;
}
List Transfers
typescript
interface ListTransfersParams {
limit?: number;
offset?: number;
starting_after?: string;
ending_before?: string;
transfer_state?: TransferState;
tx_hash?: string;
updated_after_ms?: number;
updated_before_ms?: number;
template_id?: string;
}
async function listTransfers(
customerId: string,
params: ListTransfersParams = {}
): Promise<{ count: number; data: Transfer[] }> {
const query = new URLSearchParams();
if (params.limit) query.set('limit', params.limit.toString());
if (params.offset) query.set('offset', params.offset.toString());
if (params.transfer_state) query.set('transfer_state', params.transfer_state);
if (params.starting_after) query.set('starting_after', params.starting_after);
if (params.ending_before) query.set('ending_before', params.ending_before);
const response = await fetch(
`${BRIDGE_API_URL}/transfers?${query}`,
{
headers: { 'Api-Key': API_KEY },
}
);
if (!response.ok) {
throw new Error('Failed to list transfers');
}
return response.json();
}
// Usage
const transfers = await listTransfers('cust_alice', { limit: 10 });
console.log(`Found ${transfers.count} transfers`);
transfers.data.forEach(t => {
console.log(`${t.id}: ${t.state} - ${t.amount} ${t.source.currency}`);
});
Transfer Tracking
typescript
class TransferTracker {
async getTransfer(transferId: string): Promise<Transfer> {
const response = await fetch(`${BRIDGE_API_URL}/transfers/${transferId}`, {
headers: { 'Api-Key': API_KEY },
});
if (!response.ok) {
throw new Error(`Transfer not found: ${transferId}`);
}
return response.json();
}
async waitForCompletion(transferId: string, maxAttempts: number = 30): Promise<Transfer> {
for (let i = 0; i < maxAttempts; i++) {
const transfer = await this.getTransfer(transferId);
if (transfer.state === 'completed') {
return transfer;
}
if (transfer.state === 'failed') {
throw new Error(`Transfer failed: ${transferId}`);
}
await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds
}
throw new Error('Transfer pending - timeout');
}
generateReceipt(transfer: Transfer): string {
if (!transfer.receipt) {
return 'Receipt not available';
}
return `
Transfer Receipt
=================
ID: ${transfer.id}
Status: ${transfer.state}
Date: ${transfer.created_at}
Amount: ${transfer.amount} ${transfer.source.currency}
Developer Fee: ${transfer.receipt.developer_fee}
Exchange Fee: ${transfer.receipt.exchange_fee}
Gas Fee: ${transfer.receipt.gas_fee}
Final Amount: ${transfer.receipt.final_amount} ${transfer.destination.currency}
${transfer.receipt.destination_tx_hash ? `Tx Hash: ${transfer.receipt.destination_tx_hash}` : ''}
View Full Receipt: ${transfer.receipt.url}
`.trim();
}
}
Cross-Border Payment Flow
typescript
interface CrossBorderPayment {
senderCustomerId: string;
senderCurrency: 'usd' | 'eur' | 'mxn';
recipientCryptoAddress: string;
amount: string;
}
async function processCrossBorderPayment(payment: CrossBorderPayment): Promise<Transfer> {
const paymentRail = payment.senderCurrency === 'eur' ? 'sepa' :
payment.senderCurrency === 'mxn' ? 'spei' : 'ach_push';
return createTransfer({
amount: payment.amount,
on_behalf_of: payment.senderCustomerId,
source: {
payment_rail: paymentRail,
currency: payment.senderCurrency,
},
destination: {
payment_rail: 'ethereum',
currency: 'usdc',
to_address: payment.recipientCryptoAddress,
},
});
}
// Complete remittance service
class RemittanceService {
async sendFromUSA(senderId: string, amount: string, recipientEth: string) {
return createTransfer({
amount,
on_behalf_of: senderId,
source: { payment_rail: 'ach_push', currency: 'usd' },
destination: { payment_rail: 'ethereum', currency: 'usdc', to_address: recipientEth },
});
}
async sendFromEurope(senderId: string, amount: string, recipientEth: string) {
return createTransfer({
amount,
on_behalf_of: senderId,
source: { payment_rail: 'sepa', currency: 'eur' },
destination: { payment_rail: 'ethereum', currency: 'usdc', to_address: recipientEth },
});
}
async sendFromMexico(senderId: string, amount: string, recipientEth: string) {
return createTransfer({
amount,
on_behalf_of: senderId,
source: { payment_rail: 'spei', currency: 'mxn' },
destination: { payment_rail: 'ethereum', currency: 'usdc', to_address: recipientEth },
});
}
}
Transfer Templates
typescript
interface TransferTemplate {
id: string;
state: TransferState;
on_behalf_of: string;
source: { payment_rail: PaymentRail; currency: Currency };
destination: { payment_rail: PaymentRail; currency: Currency; to_address?: string };
features: { static_template: boolean; flexible_amount?: boolean };
}
async function listTemplates(customerId: string): Promise<TransferTemplate[]> {
const response = await fetch(`${BRIDGE_API_URL}/transfer_templates?on_behalf_of=${customerId}`, {
headers: { 'Api-Key': API_KEY },
});
if (!response.ok) {
throw new Error('Failed to list templates');
}
const data = await response.json();
return data.transfer_templates || [];
}
Error Handling
typescript
class TransferError extends Error {
constructor(
message: string,
public transferId?: string,
public state?: TransferState,
public code?: string
) {
super(message);
this.name = 'TransferError';
}
}
async function handleTransferError(error: unknown): Promise<TransferError> {
if (error instanceof TransferError) {
return error;
}
const bridgeError = error as { message?: string; code?: string };
return new TransferError(
bridgeError.message || 'Unknown transfer error',
undefined,
undefined,
bridgeError.code
);
}
// Retry logic
async function retryTransfer<T>(
operation: () => Promise<T>,
maxRetries: number = 3,
delayMs: number = 1000
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await operation();
} catch (error) {
if (i === maxRetries - 1) throw error;
const transferError = error as { code?: string };
// Don't retry client errors
if (transferError.code === 'invalid_request') {
throw error;
}
await new Promise(resolve => setTimeout(resolve, delayMs * (i + 1)));
}
}
throw new Error('Unexpected retry state');
}
Webhook Events
- •
transfer.created- Transfer initiated - •
transfer.state_updated- Transfer state changed - •
transfer.completed- Transfer finished successfully - •
transfer.failed- Transfer failed
Best Practices
- •Always use Idempotency-Key - Prevents duplicate transfers
- •Store transfer IDs - Essential for tracking and support
- •Implement webhooks - More reliable than polling
- •Validate addresses - Check crypto addresses before sending
- •Handle fees - Account for developer fees in calculations
- •Support multiple rails - Different regions need different payment methods