Deposits

Overview

Once a virtual account is created and active, it can receive USD deposits via ACH or wire transfer. The Kira API provides endpoints to track deposits and their conversion to cryptocurrency.

Get Deposits

Endpoint

GET /virtual-accounts/{virtualAccountId}/deposits

Query Parameters

ParameterTypeRequiredDefaultDescription
limitintegerNo10Number of deposits to return (1-100)

Headers

HeaderRequiredDescription
AuthorizationYesBearer token from authentication
x-api-keyYesYour API key

Example Request

curl -X GET "https://api.balampay.com/virtual-accounts/va_789012345/deposits?limit=20" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "x-api-key: YOUR_API_KEY"

Response

[
  {
    "id": "dep_abc123",
    "user_id": "550e8400-e29b-41d4-a716-446655440000",
    "virtual_account_id": "va_789012345",
    "type": "payment_processed",
    "amount": "1000.00",
    "currency": "usdc",
    "created_at": "2024-01-15T14:30:00Z",
    "source": {
      "description": "ACH Push",
      "payment_rail": "ach_push",
      "sender_routing_number": "021000021",
      "sender_name": "Acme Corporation"
    },
    "destination_tx_hash": "5KYmFMZ3qvX7h8sN9Qr1pL2wJ3vT4uB6nK7xM8yE9rF1gH2jD3kL4m"
  },
  {
    "id": "dep_def456",
    "user_id": "550e8400-e29b-41d4-a716-446655440000",
    "virtual_account_id": "va_789012345",
    "type": "payment_processed",
    "amount": "500.00",
    "currency": "usdc",
    "created_at": "2024-01-14T09:15:00Z",
    "source": {
      "description": "Wire",
      "payment_rail": "wire",
      "sender_name": "John Smith"
    },
    "destination_tx_hash": "2JxK9qL5mN3oP7rS1tU4vW6xY8zA1bC2dE3fG4hI5jK6lM7nO8pQ9r"
  }
]

Response Fields

FieldTypeDescription
idstringUnique deposit event ID
user_idstringUser who owns the virtual account
virtual_account_idstringVirtual account that received the deposit
typestringEvent type - always payment_processed for deposits
amountstringAmount of cryptocurrency sent to wallet
currencystringCryptocurrency type (usdc)
created_atstringISO 8601 timestamp of when funds were received
sourceobjectInformation about the sender
source.descriptionstringPayment method description
source.payment_railstringHow funds were sent: ach_push or wire
source.sender_routing_numberstringSender's bank routing number (if available)
source.sender_namestringName of the sender
destination_tx_hashstringBlockchain transaction hash

Deposit Lifecycle

1. Funds Received

USD arrives at the virtual account via ACH or wire transfer.

Event Type: funds_received Status: Funds are pending conversion

2. Payment Submitted

Cryptocurrency transaction has been submitted to the blockchain.

Event Type: payment_submitted Status: Transaction pending blockchain confirmation

3. Payment Processed

Cryptocurrency has been confirmed and settled in the destination wallet.

Event Type: payment_processedStatus: Complete - funds are available in wallet

4. Additional States

In Review:

  • Event Type: in_review
  • Status: Transaction is under manual review for compliance
  • Action: Wait for review to complete

Refunded:

  • Event Type: refunded
  • Status: Deposit was refunded to sender
  • Reason: Usually due to compliance issues or insufficient information

Payment Rails

ACH Push (ach_push)

Standard bank transfer from a U.S. bank account.

Characteristics:

  • Speed: 1-3 business days
  • Cost: Low or free for sender
  • Limits: Typically $25,000 - $1,000,000 per transaction
  • Reversible: Can be reversed within 60 days

Example:

{
  "source": {
    "description": "ACH Push",
    "payment_rail": "ach_push",
    "sender_routing_number": "021000021",
    "sender_name": "Acme Corporation"
  }
}

Wire Transfer (wire)

Direct bank-to-bank transfer.

Characteristics:

  • Speed: Same day to next day
  • Cost: $15-$50 for sender
  • Limits: Typically unlimited
  • Reversible: Generally not reversible

Example:

{
  "source": {
    "description": "Wire",
    "payment_rail": "wire",
    "sender_name": "International Corp"
  }
}

Tracking Deposits on Blockchain

Use the destination_tx_hash to view the transaction on the blockchain explorer:

Solana

https://solscan.io/tx/{destination_tx_hash}

Polygon

https://polygonscan.com/tx/{destination_tx_hash}

Deposit Amounts and Fees

The amount field shows the final cryptocurrency amount sent to the wallet, after any fees.

Example with fees:

Original USD received: $1,000.00
Platform fee (2%): $20.00
Net USD amount: $980.00
Conversion rate: 1 USD = 1 USDC
Final USDC sent: 980.00 USDC

The deposit response shows:

{
  "amount": "980.00",
  "currency": "usdc"
}

Webhooks for Deposits

Instead of polling the deposits endpoint, set up webhooks to receive real-time notifications. See the Webhooks guide.

Deposit Webhook Payload

{
  "event": "virtual_account.deposit_funds_received",
  "data": {
    "virtual_account_id": "va_789012345",
    "amount": "1000.00",
    "currency": "usdc",
    "source": {
      "payment_rail": "ach_push",
      "sender_name": "Acme Corporation",
      "sender_bank_routing_number": "021000021",
      "trace_number": "123456789",
      "description": "Payment from sender"
    },
    "destination_tx_hash": "5KYmFMZ3qvX7h8sN...",
    "created_at": "2024-01-15T14:30:00Z"
  }
}

Common Scenarios

Scenario 1: Track All Deposits for a User

// Get all virtual accounts for user
const accounts = await fetch(
  `https://api.balampay.com/v1/users/${userId}/virtual-accounts`,
  {
    headers: { 'Authorization': `Bearer ${token}` }
  }
).then(r => r.json());

// Get deposits for each account
for (const account of accounts) {
  const deposits = await fetch(
    `https://api.balampay.com/virtual-accounts/${account.id}/deposits?limit=100`,
    {
      headers: { 'Authorization': `Bearer ${token}` }
    }
  ).then(r => r.json());

  console.log(`Account ${account.id}: ${deposits.length} deposits`);
}

Scenario 2: Calculate Total Received

const deposits = await fetch(
  `https://api.balampay.com/virtual-accounts/va_789012345/deposits?limit=100`,
  {
    headers: { 'Authorization': `Bearer ${token}` }
  }
).then(r => r.json());

const total = deposits.reduce((sum, deposit) => {
  return sum + parseFloat(deposit.amount);
}, 0);

console.log(`Total USDC received: ${total.toFixed(2)}`);

Scenario 3: Display Deposit History to User

function formatDeposit(deposit) {
  return {
    date: new Date(deposit.created_at).toLocaleDateString(),
    amount: `${deposit.amount} ${deposit.currency.toUpperCase()}`,
    sender: deposit.source.sender_name,
    method: deposit.source.payment_rail === 'ach_push' ? 'ACH' : 'Wire',
    status: 'Completed',
    transactionUrl: `https://solscan.io/tx/${deposit.destination_tx_hash}`
  };
}

const formattedDeposits = deposits.map(formatDeposit);

Testing Deposits in Sandbox

In sandbox mode, deposits must be tested using the actual banking details provided by the API. There is no endpoint to simulate deposits programmatically.

Error Responses

Invalid Virtual Account ID Format

Status: 400 Bad Request

{
  "error": "Invalid request data",
  "details": [
    {
      "path": "virtualAccountId",
      "message": "Invalid virtual account ID format",
      "code": "invalid_string"
    }
  ]
}

Virtual Account Not Found

Status: 404 Not Found

{
  "code": "not_found",
  "message": "Virtual account with ID va_789012345 not found or does not belong to client"
}

Invalid Limit Parameter

Status: 400 Bad Request

{
  "code": "validation_error",
  "message": "Limit must be a number between 1 and 100"
}

Unauthorized Access

Status: 401 Unauthorized

{
  "code": "unauthorized",
  "message": "No client ID found in claims"
}

Internal Server Error

Status: 500 Internal Server Error

{
  "code": "internal_error",
  "message": "An unexpected error occurred"
}

Best Practices

  1. Use webhooks for real-time updates - Don't poll the API frequently
  2. Store deposit IDs - Keep track of processed deposits to avoid duplicates
  3. Verify blockchain transactions - Cross-check with blockchain explorers
  4. Display sender information - Help users identify who sent the payment
  5. Show transaction hashes - Provide links to blockchain explorers
  6. Handle pagination - Use the limit parameter for accounts with many deposits
  7. Reconcile amounts - Account for fees when showing amounts to users

Troubleshooting

Deposit not showing up

Possible causes:

  1. ACH transfer still pending (takes 1-3 business days)
  2. Wire transfer being processed (check with sender's bank)
  3. Compliance review in progress
  4. Sender used incorrect bank details

Solutions:

  • Check if sender has proof of transfer
  • Verify sender used correct account/routing numbers
  • Contact support with transfer reference number

Wrong amount received

Possible causes:

  1. Platform fees deducted
  2. Sender's bank charged fees
  3. Exchange rate fluctuation during conversion

Solutions:

  • Check fee configuration in your client settings
  • Review the webhook payload for fee breakdown
  • Verify conversion rate at time of deposit

Transaction hash not working

Possible causes:

  1. Transaction still pending on blockchain
  2. Wrong blockchain explorer URL
  3. Network congestion causing delays

Solutions:

  • Wait a few minutes and try again
  • Verify using correct explorer for the blockchain
  • Check blockchain status pages for network issues

Next Steps

  1. Set up webhooks for real-time deposit notifications
  2. Handle errors gracefully in your integration
  3. Build user-facing deposit history views
  4. Implement reconciliation and reporting