Skip to main content

Xendit API - Complete Payment Gateway Integration

The Xendit API provides comprehensive payment gateway functionality for Indonesian market including bank transfers, virtual accounts, disbursements, and reconciliation. Built for enterprise-grade financial applications with webhook support, balance monitoring, and comprehensive reporting.

🎯 What Can You Manage?

Your complete payment gateway ecosystem with these powerful tools:

  • Withdrawals: Bank transfers and disbursements with real-time status tracking
  • Virtual Accounts: Fixed VA creation and management for seamless top-ups
  • Balance Operations: Multi-account balance monitoring (CASH, HOLDING, TAX)
  • Transactions: Complete transaction history with filtering and pagination
  • Reconciliation: Automated daily reconciliation with manual resolution support
  • Webhooks: Event-driven payment notifications with DLQ handling
  • Reporting: Balance and transaction reports for compliance and analytics
  • Administration: Health checks, configuration validation, and dashboard monitoring

🧩 Your Payment Building Blocks

💸 Withdrawal & Disbursement Blocks

  • POST /xendit/wallet/withdraw - Process bank withdrawal
  • GET /xendit/wallet/withdraw/status/:reference_id - Check withdrawal status

🏦 Virtual Account Blocks

  • POST /xendit/fixed-va/request - Request Fixed VA for top-up
  • GET /xendit/fixed-va/list/:wallet_id - List wallet Fixed VAs

💰 Balance & Transaction Blocks

  • GET /xendit/balance - Get account balance by type
  • GET /xendit/balance/all - Get all account balances
  • GET /xendit/transactions - List transactions with filters
  • GET /xendit/transactions/:transaction_id - Get transaction details
  • GET /xendit/transactions/count - Count transactions

📊 Report & Reconciliation Blocks

  • GET /xendit/reports/balance - Generate balance report
  • GET /xendit/reports/transactions - Generate transaction report
  • POST /xendit/reconciliation/run - Run daily reconciliation
  • POST /xendit/reconciliation/wallet/:wallet_id - Reconcile wallet
  • POST /xendit/reconciliation/resolve - Resolve discrepancy
  • GET /xendit/reconciliation/:reconciliation_id - Get reconciliation details

🔔 Webhook & Admin Blocks

  • POST /xendit/webhook - Process Xendit webhook
  • POST /xendit/webhook/dlq/process - Process failed webhooks
  • GET /xendit/webhook/stats - Webhook statistics
  • GET /xendit/health - Health check
  • GET /xendit/config/validate - Validate configuration
  • GET /xendit/admin/dashboard - Admin dashboard data

🏗️ Common Payment Patterns

Pattern 1: "I want to withdraw money to a bank account"

Balance check → processWalletWithdraw → getWithdrawStatus → Confirmation

Use: /xendit/balance/xendit/wallet/withdraw/xendit/wallet/withdraw/status/:reference_id

Pattern 2: "I want to enable VA top-ups for my wallet"

Request Fixed VA → User transfers to VA → Webhook notification → Balance update

Use: /xendit/fixed-va/request/xendit/webhook

Pattern 3: "I want to reconcile my balances"

Run reconciliation → Check discrepancies → Manual resolution if needed

Use: /xendit/reconciliation/run/xendit/reconciliation/:id/xendit/reconciliation/resolve

Pattern 4: "I want to monitor payment operations"

Check health → View dashboard → Generate reports

Use: /xendit/health/xendit/admin/dashboard/xendit/reports/transactions


💸 Withdrawal Operations

Process Wallet Withdrawal

POST/api/xendit/wallet/withdraw

Process Wallet Withdrawal to Bank

Process withdrawal from wallet to Indonesian bank account with automatic ledger integration, balance validation, and Xendit disbursement. Supports all major Indonesian banks with real-time status tracking.

Parameters

wallet_idstringrequired

Wallet ID to withdraw from

amountnumberrequired

Withdrawal amount in IDR

currencystring

Currency code (default: IDR)

bank_codestringrequired

Bank code (BCA, BNI, BRI, MANDIRI, etc.)

account_numberstringrequired

Bank account number

account_holder_namestringrequired

Account holder name

descriptionstring

Withdrawal description

user_emailstring

User email for notifications

Request Body

{
  "wallet_id": "wallet_456789",
  "amount": 500000,
  "currency": "IDR",
  "bank_code": "BCA",
  "account_number": "1234567890",
  "account_holder_name": "John Doe",
  "description": "Withdrawal to personal account",
  "user_email": "john@example.com"
}

Response

201Withdrawal initiated successfully
{
  "success": true,
  "message": "Withdrawal initiated successfully",
  "data": {
    "payout_id": "disb_1234567890",
    "reference_id": "WALLET-PAYOUT-456789-1705123456",
    "status": "PENDING",
    "amount": 500000,
    "currency": "IDR",
    "bank_code": "BCA",
    "bank_channel": "ID_BCA",
    "account_number": "1234567890",
    "account_holder_name": "John Doe",
    "estimated_arrival": "2024-01-15T12:00:00Z",
    "created_at": "2024-01-15T10:00:00Z"
  }
}
400Invalid request or insufficient balance
{
  "error": "Insufficient Xendit balance",
  "available_balance": 400000,
  "requested_amount": 500000
}
403Wallet access denied
{
  "error": "Wallet not found or access denied"
}
500Withdrawal processing failed
{
  "error": "Failed to process withdrawal",
  "details": "Xendit API error"
}

Get Withdrawal Status

GET/api/xendit/wallet/withdraw/status/:reference_id

Get Withdrawal Status

Check the current status of a withdrawal transaction using its reference ID. Returns real-time status from Xendit with transfer details.

Parameters

reference_idstringrequired

Withdrawal reference ID

Response

200Withdrawal status retrieved successfully
{
  "success": true,
  "message": "Withdrawal status retrieved successfully",
  "data": {
    "id": "disb_1234567890",
    "reference_id": "WALLET-PAYOUT-456789-1705123456",
    "status": "COMPLETED",
    "amount": 500000,
    "currency": "IDR",
    "bank_code": "BCA",
    "account_number": "1234567890",
    "account_holder_name": "John Doe",
    "completed_at": "2024-01-15T12:30:00Z"
  }
}
404Withdrawal not found
{
  "error": "Withdrawal not found"
}
500Failed to get status
{
  "error": "Failed to get withdrawal status"
}

🏦 Fixed Virtual Account Operations

Request Fixed VA

POST/api/xendit/fixed-va/request

Request Fixed Virtual Account

Request a Fixed Virtual Account for wallet top-up. Creates a permanent or single-use VA number that users can transfer to for automatic wallet funding.

Parameters

wallet_idstringrequired

Wallet ID to link VA to

bank_codestringrequired

Bank code (BCA, BNI, BRI, MANDIRI, PERMATA, CIMB)

amountnumber

Expected amount (for single-use VA)

is_single_useboolean

Single-use VA flag (default: false)

Request Body

{
  "wallet_id": "wallet_456789",
  "bank_code": "BCA",
  "is_single_use": false
}

Response

201Fixed VA created successfully
{
  "success": true,
  "message": "Fixed VA created successfully",
  "data": {
    "va_number": "1234567890123456",
    "bank_code": "BCA",
    "bank_name": "Bank Central Asia",
    "customer_name": "John Doe",
    "currency": "IDR",
    "status": "ACTIVE",
    "is_single_use": false,
    "created_at": "2024-01-15T10:00:00Z",
    "bank_info": {
      "min_amount": 10000,
      "max_amount": 50000000,
      "settlement_time": "Real-time",
      "instant_settlement": true
    }
  }
}
400Invalid request
{
  "error": "Invalid bank code or wallet not found"
}
500VA creation failed
{
  "error": "Failed to create Fixed VA"
}

Get Fixed VA List

GET/api/xendit/fixed-va/list/:wallet_id

Get Fixed VA List for Wallet

Retrieve all Fixed Virtual Accounts associated with a wallet including their status, bank details, and settlement information.

Parameters

wallet_idstringrequired

Wallet ID to get VAs for

Response

200Fixed VAs retrieved successfully
{
  "success": true,
  "message": "Fixed VAs retrieved successfully",
  "data": {
    "wallet_id": 456789,
    "total_vas": 2,
    "fixed_vas": [
      {
        "va_number": "1234567890123456",
        "bank_code": "BCA",
        "bank_name": "Bank Central Asia",
        "customer_name": "John Doe",
        "currency": "IDR",
        "status": "ACTIVE",
        "created_at": "2024-01-15T10:00:00Z",
        "bank_info": {
          "min_amount": 10000,
          "max_amount": 50000000,
          "settlement_time": "Real-time",
          "instant_settlement": true
        }
      },
      {
        "va_number": "9876543210987654",
        "bank_code": "BNI",
        "bank_name": "Bank Negara Indonesia",
        "customer_name": "John Doe",
        "currency": "IDR",
        "status": "ACTIVE",
        "created_at": "2024-01-14T09:00:00Z"
      }
    ]
  }
}
500Failed to get VA list
{
  "error": "Failed to get Fixed VA list"
}

💰 Balance Operations

Get Balance

GET/api/xendit/balance

Get Xendit Account Balance

Get Xendit account balance for a specific account type (CASH, HOLDING, or TAX). Returns current balance with account details.

Parameters

account_typestring

Account type: CASH, HOLDING, or TAX (default: CASH)

Response

200Balance retrieved successfully
{
  "success": true,
  "message": "Balance retrieved successfully",
  "data": {
    "balance": 5000000,
    "currency": "IDR",
    "account_type": "CASH"
  }
}
500Failed to get balance
{
  "error": "Failed to get balance"
}

Get All Balances

GET/api/xendit/balance/all

Get All Account Balances

Get balances for all Xendit account types (CASH, HOLDING, TAX) in a single request.

Response

200All balances retrieved successfully
{
  "success": true,
  "message": "All balances retrieved successfully",
  "data": {
    "balances": [
      {
        "account_type": "CASH",
        "balance": 5000000,
        "currency": "IDR"
      },
      {
        "account_type": "HOLDING",
        "balance": 500000,
        "currency": "IDR"
      },
      {
        "account_type": "TAX",
        "balance": 100000,
        "currency": "IDR"
      }
    ],
    "total_balance": 5600000
  }
}
500Failed to get balances
{
  "error": "Failed to get all balances"
}

📊 Transaction Operations

List Transactions

GET/api/xendit/transactions

List Transactions with Filters

List Xendit transactions with pagination and filtering support. Filter by date range, transaction types, and status.

Parameters

created_gtestring

Filter transactions created after this date (ISO 8601)

created_ltestring

Filter transactions created before this date (ISO 8601)

limitnumber

Number of results per page (default: 50, max: 100)

after_idstring

Pagination cursor (transaction ID)

typesstring

Comma-separated transaction types

statusesstring

Comma-separated status values

Response

200Transactions retrieved successfully
{
  "success": true,
  "message": "Transactions retrieved successfully",
  "data": {
    "has_more": true,
    "data": [
      {
        "id": "txn_1234567890",
        "type": "DISBURSEMENT",
        "status": "COMPLETED",
        "amount": 500000,
        "currency": "IDR",
        "reference_id": "WALLET-PAYOUT-456789-1705123456",
        "created": "2024-01-15T10:00:00Z",
        "updated": "2024-01-15T12:30:00Z"
      }
    ]
  }
}
500Failed to list transactions
{
  "error": "Failed to list transactions"
}

Get Transaction

GET/api/xendit/transactions/:transaction_id

Get Transaction Details

Get detailed information for a specific transaction by its ID.

Parameters

transaction_idstringrequired

Transaction ID to retrieve

Response

200Transaction retrieved successfully
{
  "success": true,
  "message": "Transaction retrieved successfully",
  "data": {
    "id": "txn_1234567890",
    "type": "DISBURSEMENT",
    "status": "COMPLETED",
    "amount": 500000,
    "currency": "IDR",
    "reference_id": "WALLET-PAYOUT-456789-1705123456",
    "description": "Withdrawal to personal account",
    "created": "2024-01-15T10:00:00Z",
    "updated": "2024-01-15T12:30:00Z",
    "completed": "2024-01-15T12:30:00Z",
    "bank_code": "BCA",
    "account_number": "1234567890"
  }
}
404Transaction not found
{
  "error": "Transaction not found"
}
500Failed to get transaction
{
  "error": "Failed to get transaction"
}

Get Transaction Count

GET/api/xendit/transactions/count

Get Transaction Count

Get the total count of transactions matching the specified filters.

Parameters

created_gtestring

Filter transactions created after this date

created_ltestring

Filter transactions created before this date

typesstring

Comma-separated transaction types

statusesstring

Comma-separated status values

Response

200Transaction count retrieved successfully
{
  "success": true,
  "message": "Transaction count retrieved successfully",
  "data": {
    "count": 156,
    "filters_applied": {
      "created_gte": "2024-01-01T00:00:00Z",
      "types": "DISBURSEMENT",
      "statuses": "COMPLETED"
    }
  }
}
500Failed to get transaction count
{
  "error": "Failed to get transaction count"
}

📈 Report Operations

Generate Balance Report

GET/api/xendit/reports/balance

Generate Balance Report

Generate a comprehensive balance report for a specified date range with account breakdown.

Parameters

start_datestring

Report start date (ISO 8601)

end_datestring

Report end date (ISO 8601)

formatstring

Report format: json, csv, pdf (default: json)

Response

200Balance report generated successfully
{
  "success": true,
  "message": "Balance report generated successfully",
  "data": {
    "report_period": {
      "start_date": "2024-01-01T00:00:00Z",
      "end_date": "2024-01-31T23:59:59Z"
    },
    "balances": {
      "opening_balance": 4500000,
      "closing_balance": 5000000,
      "total_inflow": 2000000,
      "total_outflow": 1500000
    },
    "account_breakdown": [
      {
        "account_type": "CASH",
        "opening": 4000000,
        "closing": 4500000,
        "inflow": 1500000,
        "outflow": 1000000
      },
      {
        "account_type": "HOLDING",
        "opening": 400000,
        "closing": 400000,
        "inflow": 400000,
        "outflow": 400000
      },
      {
        "account_type": "TAX",
        "opening": 100000,
        "closing": 100000,
        "inflow": 100000,
        "outflow": 100000
      }
    ],
    "generated_at": "2024-02-01T10:00:00Z"
  }
}
500Failed to generate report
{
  "error": "Failed to generate balance report"
}

Generate Transaction Report

GET/api/xendit/reports/transactions

Generate Transaction Report

Generate a detailed transaction report with filtering by date range and transaction types.

Parameters

start_datestring

Report start date (ISO 8601)

end_datestring

Report end date (ISO 8601)

transaction_typesstring

Comma-separated transaction types

formatstring

Report format: json, csv, pdf (default: json)

Response

200Transaction report generated successfully
{
  "success": true,
  "message": "Transaction report generated successfully",
  "data": {
    "report_period": {
      "start_date": "2024-01-01T00:00:00Z",
      "end_date": "2024-01-31T23:59:59Z"
    },
    "summary": {
      "total_transactions": 156,
      "total_amount": 78000000,
      "successful_transactions": 150,
      "failed_transactions": 6,
      "pending_transactions": 0
    },
    "by_type": [
      {
        "type": "DISBURSEMENT",
        "count": 100,
        "total_amount": 50000000,
        "success_rate": 0.98
      },
      {
        "type": "VA_PAYMENT",
        "count": 56,
        "total_amount": 28000000,
        "success_rate": 0.95
      }
    ],
    "generated_at": "2024-02-01T10:00:00Z"
  }
}
500Failed to generate report
{
  "error": "Failed to generate transaction report"
}

🔄 Reconciliation Operations

Run Daily Reconciliation

POST/api/xendit/reconciliation/run

Run Daily Reconciliation

Run automated daily reconciliation process to match internal wallet balances with Xendit account balances. Identifies discrepancies for manual review.

Parameters

datestring

Reconciliation date (default: today)

force_rerunboolean

Force rerun if already executed (default: false)

Request Body

{
  "date": "2024-01-15",
  "force_rerun": false
}

Response

200Reconciliation completed successfully
{
  "success": true,
  "message": "Daily reconciliation completed",
  "data": {
    "reconciliation_id": 123,
    "date": "2024-01-15",
    "total_wallets_checked": 45,
    "matched_wallets": 42,
    "discrepancies_found": 3,
    "total_discrepancy_amount": 15000,
    "status": "COMPLETED",
    "completed_at": "2024-01-15T10:30:00Z"
  }
}
400Invalid request or already run
{
  "error": "Reconciliation already run for this date"
}
500Reconciliation failed
{
  "error": "Failed to run reconciliation"
}

Reconcile Wallet Balance

POST/api/xendit/reconciliation/wallet/:wallet_id

Reconcile Specific Wallet

Run reconciliation for a specific wallet to verify balance accuracy against Xendit records.

Parameters

wallet_idstringrequired

Wallet ID to reconcile

force_updateboolean

Force balance update (default: false)

Request Body

{
  "force_update": false
}

Response

200Wallet reconciliation completed
{
  "success": true,
  "message": "Wallet reconciliation completed",
  "data": {
    "wallet_id": "wallet_456789",
    "internal_balance": 500000,
    "xendit_balance": 500000,
    "is_matched": true,
    "discrepancy_amount": 0,
    "last_reconciled": "2024-01-15T10:30:00Z"
  }
}
400Wallet not found
{
  "error": "Wallet not found"
}
500Reconciliation failed
{
  "error": "Failed to reconcile wallet"
}

Resolve Reconciliation

POST/api/xendit/reconciliation/resolve

Manually Resolve Reconciliation

Manually resolve a reconciliation discrepancy with notes and optional manual adjustment.

Parameters

reconciliation_idnumberrequired

Reconciliation ID to resolve

resolved_bynumberrequired

User ID who is resolving

resolution_notesstringrequired

Notes explaining the resolution

manual_adjustment_amountnumber

Manual adjustment amount if needed (default: 0)

Request Body

{
  "reconciliation_id": 123,
  "resolved_by": 1,
  "resolution_notes": "Discrepancy due to pending transaction settlement. Verified with Xendit support.",
  "manual_adjustment_amount": 0
}

Response

200Reconciliation resolved successfully
{
  "success": true,
  "message": "Reconciliation resolved successfully",
  "data": {
    "reconciliation_id": 123,
    "status": "RESOLVED",
    "resolved_by": 1,
    "resolution_notes": "Discrepancy due to pending transaction settlement",
    "manual_adjustment_amount": 0,
    "resolved_at": "2024-01-15T14:00:00Z"
  }
}
400Invalid request or already resolved
{
  "error": "Reconciliation already resolved"
}
500Resolution failed
{
  "error": "Failed to resolve reconciliation"
}

Get Reconciliation Details

GET/api/xendit/reconciliation/:reconciliation_id

Get Reconciliation Details

Get detailed information about a specific reconciliation including all discrepancies found.

Parameters

reconciliation_idstringrequired

Reconciliation ID

Response

200Reconciliation details retrieved successfully
{
  "success": true,
  "message": "Reconciliation details retrieved successfully",
  "data": {
    "reconciliation_id": 123,
    "date": "2024-01-15",
    "status": "PENDING_REVIEW",
    "total_wallets_checked": 45,
    "matched_wallets": 42,
    "discrepancies_found": 3,
    "total_discrepancy_amount": 15000,
    "discrepancies": [
      {
        "wallet_id": "wallet_111",
        "internal_balance": 100000,
        "xendit_balance": 105000,
        "discrepancy": 5000
      },
      {
        "wallet_id": "wallet_222",
        "internal_balance": 200000,
        "xendit_balance": 195000,
        "discrepancy": -5000
      },
      {
        "wallet_id": "wallet_333",
        "internal_balance": 50000,
        "xendit_balance": 55000,
        "discrepancy": 5000
      }
    ],
    "completed_at": "2024-01-15T10:30:00Z"
  }
}
404Reconciliation not found
{
  "error": "Reconciliation not found"
}
500Failed to get details
{
  "error": "Failed to get reconciliation details"
}

🔔 Webhook Operations

Process Webhook

POST/api/xendit/webhook

Process Xendit Webhook

Process incoming webhook from Xendit servers. This endpoint is called automatically by Xendit when payment events occur. Includes signature verification and DLQ handling for failed processing.

Parameters

eventstringrequired

Webhook event type (e.g., virtual_account.paid, disbursement.completed)

dataobjectrequired

Webhook payload data from Xendit

Request Body

{
  "event": "virtual_account.paid",
  "data": {
    "payment_id": "pay_1234567890",
    "external_id": "WALLET-TOPUP-456789",
    "account_number": "1234567890123456",
    "bank_code": "BCA",
    "amount": 1000000,
    "currency": "IDR",
    "status": "COMPLETED",
    "paid_at": "2024-01-15T10:00:00Z"
  }
}

Response

200Webhook processed successfully
{
  "success": true,
  "message": "Webhook processed successfully",
  "webhook_id": "whk_1234567890",
  "processed_at": "2024-01-15T10:00:01Z"
}

Process Dead Letter Queue

POST/api/xendit/webhook/dlq/process

Process Webhook Dead Letter Queue

Manually process failed webhooks from the dead letter queue. Retries webhook processing for events that failed initial processing.

Response

200DLQ processing completed
{
  "success": true,
  "message": "DLQ processing completed",
  "data": {
    "total_processed": 5,
    "successful": 4,
    "failed": 1,
    "remaining_in_queue": 1
  }
}
500DLQ processing failed
{
  "error": "Failed to process dead letter queue"
}

Get Webhook Statistics

GET/api/xendit/webhook/stats

Get Webhook Statistics

Get webhook processing statistics including success rates, event types, and processing times.

Parameters

start_datestring

Statistics start date (ISO 8601)

end_datestring

Statistics end date (ISO 8601)

Response

200Webhook statistics retrieved successfully
{
  "success": true,
  "message": "Webhook statistics retrieved successfully",
  "data": {
    "period": {
      "start_date": "2024-01-01T00:00:00Z",
      "end_date": "2024-01-31T23:59:59Z"
    },
    "total_webhooks": 1250,
    "successful": 1230,
    "failed": 20,
    "success_rate": 0.984,
    "by_event_type": [
      {
        "event": "virtual_account.paid",
        "count": 800,
        "success_rate": 0.99
      },
      {
        "event": "disbursement.completed",
        "count": 400,
        "success_rate": 0.975
      },
      {
        "event": "disbursement.failed",
        "count": 50,
        "success_rate": 0.96
      }
    ],
    "avg_processing_time_ms": 150,
    "dlq_count": 5
  }
}
500Failed to get statistics
{
  "error": "Failed to get webhook statistics"
}

🔧 Administrative Operations

Health Check

GET/api/xendit/health

Xendit Integration Health Check

Perform comprehensive health check for Xendit integration including configuration validation, API connectivity, and database connection.

Response

200Xendit integration is healthy
{
  "success": true,
  "message": "Xendit integration is healthy",
  "data": {
    "service": "xendit_integration",
    "status": "healthy",
    "timestamp": "2024-01-15T10:00:00Z",
    "checks": {
      "configuration": {
        "status": "pass",
        "issues": []
      },
      "api_connectivity": {
        "status": "pass",
        "message": "Xendit API connection successful",
        "error": null
      },
      "database": {
        "status": "pass",
        "message": "Database connection active"
      }
    }
  }
}
503Xendit integration is unhealthy
{
  "success": false,
  "message": "Xendit integration is unhealthy",
  "data": {
    "service": "xendit_integration",
    "status": "unhealthy",
    "error": "Configuration validation failed"
  }
}

Validate Configuration

GET/api/xendit/config/validate

Validate Xendit Configuration

Validate Xendit API configuration including API keys, webhook tokens, and base URL settings.

Response

200Configuration validation result
{
  "success": true,
  "message": "Configuration is valid",
  "data": {
    "valid": true,
    "issues": [],
    "timestamp": "2024-01-15T10:00:00Z"
  }
}
500Validation failed
{
  "success": false,
  "message": "Configuration has issues",
  "data": {
    "valid": false,
    "issues": [
      "API_KEY_XENDIT not set",
      "WEBHOOK_TOKEN_XENDIT_TEST not set"
    ],
    "timestamp": "2024-01-15T10:00:00Z"
  }
}

Get Dashboard Data

GET/api/xendit/admin/dashboard

Get Admin Dashboard Data

Get comprehensive dashboard data for monitoring Xendit operations including balances, webhook stats, and connectivity status.

Response

200Dashboard data retrieved successfully
{
  "success": true,
  "message": "Dashboard data retrieved successfully",
  "data": {
    "balances": {
      "balances": [
        {
          "account_type": "CASH",
          "balance": 5000000,
          "currency": "IDR"
        },
        {
          "account_type": "HOLDING",
          "balance": 500000,
          "currency": "IDR"
        },
        {
          "account_type": "TAX",
          "balance": 100000,
          "currency": "IDR"
        }
      ],
      "total_balance": 5600000
    },
    "webhook_stats": {
      "total_webhooks": 1250,
      "successful": 1230,
      "failed": 20,
      "success_rate": 0.984,
      "dlq_count": 5
    },
    "connectivity": {
      "success": true,
      "message": "Xendit API connection successful",
      "balance_retrieved": true
    },
    "last_updated": "2024-01-15T10:00:00Z"
  }
}
500Failed to get dashboard data
{
  "error": "Failed to get dashboard data"
}

🚀 Complete Xendit Workflows

Workflow 1: Complete Withdrawal Process

// Complete withdrawal workflow with status tracking
async function completeWithdrawal(walletId, amount, bankDetails) {
console.log('Starting withdrawal process...');

// Step 1: Check Xendit balance
const balance = await brdzSDK.xendit.getBalance();

if (balance.data.balance < amount) {
throw new Error(`Insufficient Xendit balance. Available: ${balance.data.balance}, Required: ${amount}`);
}

console.log(`Balance check passed: IDR ${balance.data.balance.toLocaleString()}`);

// Step 2: Process withdrawal
const withdrawal = await brdzSDK.xendit.processWalletWithdraw({
wallet_id: walletId,
amount: amount,
currency: 'IDR',
bank_code: bankDetails.bank_code,
account_number: bankDetails.account_number,
account_holder_name: bankDetails.account_holder_name,
description: 'User withdrawal request',
user_email: bankDetails.email
});

console.log(`Withdrawal initiated: ${withdrawal.data.payout_id}`);
console.log(`Reference: ${withdrawal.data.reference_id}`);

// Step 3: Track status until completion
let status = 'PENDING';
let attempts = 0;
const maxAttempts = 60; // 5 minutes with 5-second intervals

while (status === 'PENDING' && attempts < maxAttempts) {
await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds

const statusCheck = await brdzSDK.xendit.getWithdrawStatus(withdrawal.data.reference_id);
status = statusCheck.data.status;

console.log(`Status check ${attempts + 1}: ${status}`);
attempts++;
}

if (status === 'COMPLETED') {
console.log('Withdrawal completed successfully!');
return {
success: true,
payout_id: withdrawal.data.payout_id,
reference_id: withdrawal.data.reference_id,
status: status
};
} else if (status === 'FAILED') {
console.error('Withdrawal failed!');
return {
success: false,
error: 'Withdrawal failed',
reference_id: withdrawal.data.reference_id
};
} else {
console.log('Withdrawal still pending after timeout');
return {
success: false,
error: 'Timeout waiting for completion',
reference_id: withdrawal.data.reference_id,
status: status
};
}
}

Workflow 2: Fixed VA Management

// Complete Fixed VA setup and monitoring
async function setupFixedVAForWallet(walletId, banks = ['BCA', 'BNI', 'MANDIRI']) {
console.log('Setting up Fixed VAs for wallet...');

const createdVAs = [];

// Create Fixed VA for each bank
for (const bankCode of banks) {
try {
const va = await brdzSDK.xendit.requestFixedVA({
wallet_id: walletId,
bank_code: bankCode,
is_single_use: false
});

createdVAs.push({
bank: bankCode,
va_number: va.data.va_number,
status: va.data.status
});

console.log(`${bankCode} VA created: ${va.data.va_number}`);
} catch (error) {
console.error(`❌ Failed to create ${bankCode} VA:`, error.message);
}
}

// Get complete VA list
const vaList = await brdzSDK.xendit.getFixedVAList(walletId);

console.log(`\nTotal VAs for wallet: ${vaList.data.total_vas}`);

return {
created_vas: createdVAs,
all_vas: vaList.data.fixed_vas
};
}

Workflow 3: Daily Reconciliation Process

// Automated daily reconciliation with discrepancy resolution
async function performDailyReconciliation(adminUserId) {
console.log('Starting daily reconciliation...');

// Step 1: Run reconciliation
const reconciliation = await brdzSDK.xendit.runDailyReconciliation();

console.log(`Reconciliation ID: ${reconciliation.data.reconciliation_id}`);
console.log(`Wallets Checked: ${reconciliation.data.total_wallets_checked}`);
console.log(`Matched: ${reconciliation.data.matched_wallets}`);
console.log(`Discrepancies: ${reconciliation.data.discrepancies_found}`);

// Step 2: If discrepancies found, get details
if (reconciliation.data.discrepancies_found > 0) {
const details = await brdzSDK.xendit.getReconciliationDetails(
reconciliation.data.reconciliation_id
);

console.log('\n🔍 Discrepancy Details:');
details.data.discrepancies.forEach(disc => {
console.log(`\nWallet: ${disc.wallet_id}`);
console.log(` Internal: IDR ${disc.internal_balance.toLocaleString()}`);
console.log(` Xendit: IDR ${disc.xendit_balance.toLocaleString()}`);
console.log(` Difference: IDR ${disc.discrepancy.toLocaleString()}`);
});

// Step 3: Auto-resolve small discrepancies (< 1000 IDR)
for (const disc of details.data.discrepancies) {
if (Math.abs(disc.discrepancy) < 1000) {
console.log(`\n⚡ Auto-resolving small discrepancy for ${disc.wallet_id}`);

await brdzSDK.xendit.resolveReconciliation({
reconciliation_id: reconciliation.data.reconciliation_id,
resolved_by: adminUserId,
resolution_notes: `Auto-resolved: Small discrepancy within tolerance (${disc.discrepancy} IDR)`,
manual_adjustment_amount: 0
});
} else {
console.log(`\n⚠️ Manual review required for ${disc.wallet_id} (${disc.discrepancy} IDR)`);
}
}
} else {
console.log('\n✅ All wallets matched! No discrepancies found.');
}

return reconciliation.data;
}

Workflow 4: Monitoring Dashboard

// Complete monitoring dashboard
async function displayMonitoringDashboard() {
console.log('=== XENDIT MONITORING DASHBOARD ===\n');

// Get all dashboard data
const dashboard = await brdzSDK.xendit.getDashboardData();

// Display balances
console.log('💰 ACCOUNT BALANCES');
console.log(`Total: IDR ${dashboard.data.balances.total_balance.toLocaleString()}`);
dashboard.data.balances.balances.forEach(acc => {
console.log(` ${acc.account_type.padEnd(8)}: IDR ${acc.balance.toLocaleString()}`);
});

// Display webhook stats
console.log('\n🔔 WEBHOOK STATISTICS');
const stats = dashboard.data.webhook_stats;
console.log(`Total Processed: ${stats.total_webhooks}`);
console.log(`Success Rate: ${(stats.success_rate * 100).toFixed(2)}%`);
console.log(`Failed: ${stats.failed}`);
console.log(`In DLQ: ${stats.dlq_count}`);

// Health status
console.log('\n🏥 SYSTEM HEALTH');
const health = await brdzSDK.xendit.healthCheck();
console.log(`Status: ${health.data.status.toUpperCase()}`);

Object.entries(health.data.checks).forEach(([check, result]) => {
const icon = result.status === 'pass' ? '✅' : '❌';
console.log(` ${icon} ${check}: ${result.status}`);
});

// Recent transactions
console.log('\n📊 RECENT TRANSACTIONS');
const transactions = await brdzSDK.xendit.listTransactions({
limit: 5
});

transactions.data.data.forEach(txn => {
console.log(` ${txn.type}: IDR ${txn.amount.toLocaleString()} - ${txn.status}`);
});

// Check for DLQ items
if (stats.dlq_count > 0) {
console.log('\n⚠️ WARNING: Failed webhooks in DLQ!');
console.log(' Run processDLQ() to retry failed webhooks');
}

console.log(`\n📅 Last Updated: ${dashboard.data.last_updated}`);
}

Workflow 5: Error Handling Best Practices

// Robust error handling for Xendit operations
class XenditErrorHandler {
static async safeWithdraw(walletId, amount, bankDetails) {
try {
return await brdzSDK.xendit.processWalletWithdraw({
wallet_id: walletId,
amount: amount,
currency: 'IDR',
...bankDetails
});
} catch (error) {
return this.handleWithdrawError(error);
}
}

static handleWithdrawError(error) {
const errorMessage = error.message || error.toString();

if (errorMessage.includes('Insufficient Xendit balance')) {
return {
success: false,
error_code: 'INSUFFICIENT_BALANCE',
message: 'Not enough balance in Xendit account',
suggestion: 'Top up Xendit balance before processing withdrawals',
retry: false
};
} else if (errorMessage.includes('Invalid bank code')) {
return {
success: false,
error_code: 'INVALID_BANK',
message: 'Bank code not supported',
suggestion: 'Use valid Indonesian bank code (BCA, BNI, BRI, MANDIRI, etc.)',
retry: false
};
} else if (errorMessage.includes('Wallet not found')) {
return {
success: false,
error_code: 'WALLET_NOT_FOUND',
message: 'Wallet does not exist or access denied',
suggestion: 'Verify wallet ID and user permissions',
retry: false
};
} else if (errorMessage.includes('Xendit API')) {
return {
success: false,
error_code: 'XENDIT_API_ERROR',
message: 'Xendit service temporarily unavailable',
suggestion: 'Retry after a few minutes',
retry: true
};
} else {
return {
success: false,
error_code: 'UNKNOWN_ERROR',
message: errorMessage,
suggestion: 'Check logs and contact support if issue persists',
retry: false
};
}
}

static async safeReconciliation() {
try {
return await brdzSDK.xendit.runDailyReconciliation();
} catch (error) {
console.error('Reconciliation failed:', error.message);

// Send alert to admin
await this.notifyAdmin('Reconciliation Failed', {
error: error.message,
timestamp: new Date().toISOString()
});

return {
success: false,
error: error.message
};
}
}

static async notifyAdmin(subject, data) {
// Implement your notification logic here
console.log(`🚨 ADMIN ALERT: ${subject}`);
console.log(JSON.stringify(data, null, 2));
}
}

// Usage
const result = await XenditErrorHandler.safeWithdraw(
'wallet_456789',
500000,
{
bank_code: 'BCA',
account_number: '1234567890',
account_holder_name: 'John Doe'
}
);

if (!result.success) {
console.error(`Error: ${result.message}`);
console.log(`Suggestion: ${result.suggestion}`);

if (result.retry) {
console.log('This error is temporary, safe to retry');
}
}

🔐 Authentication & Authorization

All Xendit endpoints require authentication and specific role permissions:

// Configure authentication
const config = await brdzSDK.config;
config.setToken('your-jwt-token');
config.setApiKey('your-api-key');

// User-level operations (user or admin role)
await brdzSDK.xendit.processWalletWithdraw(data);
await brdzSDK.xendit.getWithdrawStatus(reference_id);
await brdzSDK.xendit.requestFixedVA(data);
await brdzSDK.xendit.getFixedVAList(wallet_id);
await brdzSDK.xendit.reconcileWalletBalance(wallet_id);
await brdzSDK.xendit.getReconciliationDetails(id);

// Admin-only operations (admin role required)
await brdzSDK.xendit.getBalance();
await brdzSDK.xendit.getAllBalances();
await brdzSDK.xendit.listTransactions();
await brdzSDK.xendit.getTransaction(id);
await brdzSDK.xendit.getTransactionCount();
await brdzSDK.xendit.generateBalanceReport();
await brdzSDK.xendit.generateTransactionReport();
await brdzSDK.xendit.runDailyReconciliation();
await brdzSDK.xendit.resolveReconciliation(data);
await brdzSDK.xendit.processDLQ();
await brdzSDK.xendit.getWebhookStats();
await brdzSDK.xendit.validateConfiguration();
await brdzSDK.xendit.getDashboardData();

// Public operations (no auth required)
await brdzSDK.xendit.healthCheck();
await brdzSDK.xendit.processWebhook(data); // Called by Xendit servers

🌍 Supported Banks & Features

Indonesian Bank Support

  • BCA (Bank Central Asia) - Real-time settlement
  • BNI (Bank Negara Indonesia) - Real-time settlement
  • BRI (Bank Rakyat Indonesia) - Real-time settlement
  • MANDIRI (Bank Mandiri) - Real-time settlement
  • PERMATA (Bank Permata) - Real-time settlement
  • CIMB (CIMB Niaga) - Real-time settlement

Virtual Account Features

  • Fixed VA: Permanent VA numbers for recurring top-ups
  • Single-use VA: One-time VA with specific amount
  • Multi-bank support: Create VAs for multiple banks
  • Real-time settlement: Instant wallet funding on payment
  • Webhook notifications: Automatic payment notifications

Disbursement Features

  • Bank transfers: Support for all major Indonesian banks
  • Real-time status: Track withdrawal status in real-time
  • Balance validation: Automatic balance checking before disbursement
  • Ledger integration: Automatic ledger updates
  • Fee transparency: Clear fee breakdown

💡 Pro Tips for Xendit Integration

Balance Management

// Always check balance before withdrawals
async function safeWithdrawal(walletId, amount, bankDetails) {
const balance = await brdzSDK.xendit.getBalance();

if (balance.data.balance < amount) {
throw new Error(`Insufficient balance: ${balance.data.balance} < ${amount}`);
}

return await brdzSDK.xendit.processWalletWithdraw({
wallet_id: walletId,
amount: amount,
...bankDetails
});
}

Webhook Monitoring

// Regular webhook health monitoring
async function monitorWebhooks() {
const stats = await brdzSDK.xendit.getWebhookStats({
start_date: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString()
});

if (stats.data.success_rate < 0.95) {
console.warn('⚠️ Webhook success rate below 95%');
}

if (stats.data.dlq_count > 10) {
console.warn('⚠️ More than 10 webhooks in DLQ');
await brdzSDK.xendit.processDLQ();
}
}

Reconciliation Automation

// Schedule daily reconciliation
const cron = require('node-cron');

// Run at 2 AM every day
cron.schedule('0 2 * * *', async () => {
console.log('Running daily reconciliation...');
await performDailyReconciliation(adminUserId);
});

Webhook Configuration

Configure your webhook URL in Xendit Dashboard to point to https://api.brdz.link/api/xendit/webhook. Ensure your server can handle POST requests from Xendit's IP addresses.

Balance Monitoring

Always monitor your Xendit balance to avoid withdrawal failures. Set up alerts when balance falls below threshold (e.g., 10M IDR).

Reconciliation Best Practice

Run daily reconciliation during off-peak hours (e.g., 2-4 AM). Auto-resolve small discrepancies (< 1000 IDR) and flag larger ones for manual review.