Skip to main content

Indodax API - Complete Crypto Conversion Integration

The Indodax API provides comprehensive cryptocurrency conversion functionality for Indonesian market including IDR to USDC conversion, automated trading, crypto wallet management, and real-time job tracking. Built for enterprise-grade crypto operations with multi-stage conversion flow and automated withdrawal support.

🎯 What Can You Manage?

Your complete crypto conversion ecosystem with these powerful tools:

  • Crypto Wallets: Multi-network wallet management (ERC-20, TRC-20, BEP-20, Polygon, Solana)
  • Conversion Jobs: Automated IDR to USDC conversion with multi-stage processing
  • Balance Polling: Real-time Indodax balance monitoring and confirmation
  • Trade Execution: Automated USDC trading on Indodax exchange
  • Job Tracking: Complete conversion job status monitoring and history
  • Network Support: Multiple blockchain networks for USDC withdrawal
  • Admin Operations: Batch conversion processing and manual job execution

🧩 Your Crypto Conversion Building Blocks

💼 Crypto Wallet Blocks

  • POST /indodax/crypto-wallet/save - Save or update crypto wallet
  • GET /indodax/crypto-wallet/list - List all user wallets
  • GET /indodax/crypto-wallet/primary - Get primary wallet
  • POST /indodax/crypto-wallet/delete - Delete crypto wallet
  • GET /indodax/crypto-wallet/networks - Get supported networks

🔄 Conversion Operations Blocks

  • POST /indodax/conversion/run-daily - Trigger daily conversion (Admin)
  • POST /indodax/conversion/execute/:job_id - Execute specific job (Admin)
  • GET /indodax/conversion/pending - Get pending conversions (Admin)
  • POST /indodax/conversion/poll-balance - Run balance polling (Admin)
  • POST /indodax/conversion/poll-job/:job_id - Poll specific job (Admin)
  • POST /indodax/conversion/run-trades - Run trade execution (Admin)
  • POST /indodax/conversion/execute-trade/:job_id - Execute specific trade (Admin)
  • GET /indodax/conversion/status/:job_id - Get job status

💸 Disbursement Blocks

  • POST /indodax/disbursement/start - Start manual disbursement to Indodax

🏗️ Common Conversion Patterns

Pattern 1: "I want to add my crypto wallet for USDC withdrawal"

Add wallet → Set as primary → Verify network support

Use: /indodax/crypto-wallet/save/indodax/crypto-wallet/primary

Pattern 2: "I want to convert IDR to USDC automatically"

Daily conversion trigger → Balance polling → Trade execution → USDC withdrawal

Use: /indodax/conversion/run-daily/indodax/conversion/poll-balance/indodax/conversion/run-trades

Pattern 3: "I want to track my conversion job status"

Get job status → Monitor progress → Check completion

Use: /indodax/conversion/status/:job_id

Pattern 4: "I want to manage pending conversions (Admin)"

Get pending list → Execute specific jobs → Monitor completion

Use: /indodax/conversion/pending/indodax/conversion/execute/:job_id/indodax/conversion/status/:job_id


💼 Crypto Wallet Operations

Save Crypto Wallet

POST/api/indodax/crypto-wallet/save

Save or Update Crypto Wallet

Save or update user's crypto wallet address for USDC withdrawal. Supports multiple blockchain networks (ERC-20, TRC-20, BEP-20, Polygon, Solana) with format validation. Automatically updates existing wallet if network already registered.

Parameters

wallet_addressstringrequired

Crypto wallet address

networkstringrequired

Network type: ERC20, TRC20, BEP20, POLYGON, SOLANA

set_as_primaryboolean

Set as primary wallet (default: true)

Request Body

{
  "wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
  "network": "ERC20",
  "set_as_primary": true
}

Response

200Crypto wallet updated successfully
{
  "success": true,
  "message": "Crypto wallet updated successfully",
  "data": {
    "bw_id": 123,
    "wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
    "network": "Ethereum (ERC-20)",
    "chain_id": "erc20",
    "is_primary": true,
    "action": "updated"
  }
}
201Crypto wallet created successfully
{
  "success": true,
  "message": "Crypto wallet created successfully",
  "data": {
    "bw_id": 123,
    "wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
    "network": "Ethereum (ERC-20)",
    "chain_id": "erc20",
    "is_primary": true,
    "action": "created"
  },
  "timestamp": "2024-01-15T10:00:00Z"
}
400Invalid wallet format or missing fields
{
  "success": false,
  "error_code": "INVALID_WALLET_FORMAT",
  "message": "Invalid Ethereum (ERC-20) address format",
  "details": {
    "example": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
    "supported": [
      "ERC20",
      "TRC20",
      "BEP20",
      "POLYGON",
      "SOLANA"
    ]
  }
}
500Failed to save wallet
{
  "error": "Failed to save crypto wallet"
}
curl -X POST https://api.brdz.link/api/indodax/crypto-wallet/save \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
  "wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
  "network": "ERC20",
  "set_as_primary": true
}'

Get Crypto Wallet List

GET/api/indodax/crypto-wallet/list

Get User's Crypto Wallets

Retrieve all active crypto wallets for the authenticated user including primary wallet designation. Returns wallets sorted by primary status and creation date.

Response

200Crypto wallets retrieved successfully
{
  "success": true,
  "message": "Crypto wallets retrieved successfully",
  "data": {
    "total": 3,
    "wallets": [
      {
        "bw_id": 123,
        "wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
        "network": "Ethereum (ERC-20)",
        "chain_id": "erc20",
        "is_primary": true,
        "status": "ACTIVE",
        "created_at": "2024-01-15T10:00:00Z"
      },
      {
        "bw_id": 124,
        "wallet_address": "TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9",
        "network": "Tron (TRC-20)",
        "chain_id": "trc20",
        "is_primary": false,
        "status": "ACTIVE",
        "created_at": "2024-01-14T09:00:00Z"
      }
    ],
    "primary_wallet": {
      "bw_id": 123,
      "wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
      "network": "Ethereum (ERC-20)",
      "chain_id": "erc20"
    }
  },
  "timestamp": "2024-01-15T10:30:00Z"
}
500Failed to fetch wallets
{
  "error": "Failed to fetch crypto wallets"
}
curl -X GET https://api.brdz.link/api/indodax/crypto-wallet/list \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "x-api-key: YOUR_API_KEY"

Get Primary Wallet

GET/api/indodax/crypto-wallet/primary

Get Primary Crypto Wallet

Get user's primary crypto wallet used for USDC withdrawals in conversion process. Returns error if no primary wallet is set.

Response

200Primary wallet retrieved successfully
{
  "success": true,
  "message": "Primary wallet retrieved successfully",
  "data": {
    "bw_id": 123,
    "wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
    "network": "Ethereum (ERC-20)",
    "chain_id": "erc20"
  },
  "timestamp": "2024-01-15T10:00:00Z"
}
404No primary wallet found
{
  "success": false,
  "error_code": "NO_PRIMARY_WALLET",
  "message": "No primary wallet found. Please add a crypto wallet first.",
  "timestamp": "2024-01-15T10:00:00Z"
}
500Failed to fetch primary wallet
{
  "error": "Failed to fetch primary wallet"
}
curl -X GET https://api.brdz.link/api/indodax/crypto-wallet/primary \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "x-api-key: YOUR_API_KEY"

Delete Crypto Wallet

POST/api/indodax/crypto-wallet/delete

Delete Crypto Wallet

Soft delete (deactivate) a crypto wallet. Wallet is marked as inactive but not permanently removed from database.

Parameters

bw_idnumberrequired

Blockchain wallet ID to delete

Request Body

{
  "bw_id": 123
}

Response

200Wallet deleted successfully
{
  "success": true,
  "message": "Wallet deleted successfully",
  "timestamp": "2024-01-15T10:00:00Z"
}
400Invalid wallet ID
{
  "success": false,
  "error_code": "INVALID_WALLET_ID",
  "message": "Valid wallet ID is required"
}
404Wallet not found
{
  "success": false,
  "error_code": "WALLET_NOT_FOUND",
  "message": "Wallet not found or already deleted"
}
500Failed to delete wallet
{
  "error": "Failed to delete crypto wallet"
}
curl -X POST https://api.brdz.link/api/indodax/crypto-wallet/delete \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
  "bw_id": 123
}'

Get Supported Networks

GET/api/indodax/crypto-wallet/networks

Get Supported Networks

Get list of all supported blockchain networks for USDC withdrawal including format examples and Indodax network codes.

Response

200Supported networks retrieved successfully
{
  "success": true,
  "message": "Supported networks retrieved successfully",
  "data": {
    "total": 5,
    "networks": [
      {
        "code": "ERC20",
        "name": "Ethereum (ERC-20)",
        "example": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
        "indodax_code": "erc20"
      },
      {
        "code": "TRC20",
        "name": "Tron (TRC-20)",
        "example": "TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9",
        "indodax_code": "trc20"
      },
      {
        "code": "BEP20",
        "name": "BSC (BEP-20)",
        "example": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
        "indodax_code": "bep20"
      },
      {
        "code": "POLYGON",
        "name": "Polygon",
        "example": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
        "indodax_code": "polygon"
      },
      {
        "code": "SOLANA",
        "name": "Solana",
        "example": "7EcDhSYGxXyscszYEp35KHN8vvw3svAuLKTzXwCFLtV",
        "indodax_code": "solana"
      }
    ]
  },
  "timestamp": "2024-01-15T10:00:00Z"
}
500Failed to fetch networks
{
  "error": "Failed to fetch supported networks"
}
curl -X GET https://api.brdz.link/api/indodax/crypto-wallet/networks \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "x-api-key: YOUR_API_KEY"

💸 Disbursement Operations

Start Disbursement to Indodax

POST/api/indodax/disbursement/start

Start Manual Disbursement to Indodax

Manually trigger disbursement from Xendit balance to Indodax VA for crypto conversion. User can initiate their own disbursement without waiting for daily cron. Performs pre-disbursement reconciliation, creates conversion job, and initiates Xendit payout.

Parameters

wallet_idnumberrequired

User's IDR wallet ID

amount_idrnumberrequired

Amount in IDR (minimum 200,000)

Request Body

{
  "wallet_id": 59,
  "amount_idr": 500000
}

Response

200Disbursement initiated successfully
{
  "success": true,
  "message": "Disbursement to Indodax initiated successfully",
  "data": {
    "job_id": 123,
    "disbursement_id": "disb_1234567890abcdef",
    "reference_id": "INDODAX-DISB-123-1705308000000",
    "amount_idr": 500000,
    "status": "DISBURSING",
    "reconciliation": {
      "reconciliation_id": 456,
      "status": "MATCHED",
      "requires_admin_attention": false
    }
  },
  "timestamp": "2024-01-15T10:00:00Z"
}
400Invalid request or amount below minimum
{
  "success": false,
  "error_code": "AMOUNT_BELOW_MINIMUM",
  "message": "Minimum disbursement amount is IDR 200,000",
  "timestamp": "2024-01-15T10:00:00Z"
}
403Reconciliation failed
{
  "success": false,
  "error_code": "RECONCILIATION_FAILED",
  "message": "Pre-disbursement reconciliation failed",
  "reconciliation_id": 789,
  "details": {
    "ledger_balance": 5000000,
    "xendit_balance": 4900000,
    "difference_amount": 100000
  },
  "timestamp": "2024-01-15T10:00:00Z"
}
500Disbursement failed
{
  "success": false,
  "error_code": "DISBURSEMENT_FAILED",
  "message": "Failed to start disbursement",
  "details": "Xendit API error",
  "timestamp": "2024-01-15T10:00:00Z"
}
curl -X POST https://api.brdz.link/api/indodax/disbursement/start \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
  "wallet_id": 59,
  "amount_idr": 500000
}'

🔄 Conversion Operations

Run Daily Conversion

POST/api/indodax/conversion/run-daily

Run Daily Conversion Process

Trigger automated daily conversion process for all eligible users. Creates conversion jobs, initiates Xendit disbursements to Indodax VA, and starts the conversion pipeline. Admin only operation, typically run by cron job.

Response

200Daily conversion completed successfully
{
  "success": true,
  "message": "Daily conversion completed successfully",
  "data": {
    "total_users": 45,
    "jobs_created": 42,
    "total_idr": 150000000,
    "disbursements_initiated": 42,
    "failed_jobs": 0,
    "processing_time_ms": 8500,
    "summary": {
      "pending": 42,
      "waiting_indodax": 0,
      "trading": 0,
      "completed": 0,
      "failed": 0
    }
  },
  "timestamp": "2024-01-15T02:00:00Z"
}
500Daily conversion failed
{
  "success": false,
  "error_code": "DAILY_CONVERSION_FAILED",
  "message": "Failed to run daily conversion",
  "details": "Xendit API error"
}
curl -X POST https://api.brdz.link/api/indodax/conversion/run-daily \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "x-api-key: YOUR_API_KEY"

Execute Conversion Job

POST/api/indodax/conversion/execute/{job_id}

Execute Specific Conversion Job

Manually execute a specific conversion job by ID. Runs pre-disbursement reconciliation, initiates Xendit disbursement to Indodax VA, and updates job status. Admin only operation.

Parameters

job_idnumberrequired

Conversion job ID to execute (path parameter)

Response

200Conversion job executed successfully
{
  "success": true,
  "message": "Conversion job executed successfully",
  "data": {
    "job_id": 123,
    "user_id": 456,
    "batch_amount_idr": 1000000,
    "disbursement_id": "disb_1234567890",
    "status": "WAITING_INDODAX",
    "reconciliation_passed": true,
    "executed_at": "2024-01-15T10:00:00Z"
  },
  "timestamp": "2024-01-15T10:00:01Z"
}
400Invalid job ID or reconciliation failed
{
  "success": false,
  "error_code": "RECONCILIATION_FAILED",
  "message": "Pre-disbursement reconciliation failed",
  "reconciliation_id": 789,
  "details": {
    "ledger_balance": 5000000,
    "xendit_balance": 4900000,
    "difference_amount": 100000
  }
}
500Job execution failed
{
  "success": false,
  "error_code": "JOB_EXECUTION_FAILED",
  "message": "Failed to execute conversion job"
}
curl -X POST https://api.brdz.link/api/indodax/conversion/execute/123 \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "x-api-key: YOUR_API_KEY"

Get Pending Conversions

GET/api/indodax/conversion/pending

Get Pending Conversions

Get list of all users with pending conversions eligible for processing. Returns user details and conversion amounts. Admin only operation.

Response

200Pending conversions retrieved successfully
{
  "success": true,
  "message": "Pending conversions retrieved successfully",
  "data": {
    "total": 5,
    "users": [
      {
        "user_id": 456,
        "email": "user1@example.com",
        "total_amount_idr": 2000000,
        "eligible_for_conversion": true,
        "has_primary_wallet": true,
        "wallet_network": "Ethereum (ERC-20)"
      },
      {
        "user_id": 457,
        "email": "user2@example.com",
        "total_amount_idr": 1500000,
        "eligible_for_conversion": true,
        "has_primary_wallet": true,
        "wallet_network": "Tron (TRC-20)"
      }
    ]
  },
  "timestamp": "2024-01-15T10:00:00Z"
}
500Failed to fetch pending conversions
{
  "error": "Failed to fetch pending conversions"
}
curl -X GET https://api.brdz.link/api/indodax/conversion/pending \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "x-api-key: YOUR_API_KEY"

Run Balance Polling

POST/api/indodax/conversion/poll-balance

Run Indodax Balance Polling

Poll Indodax account balance to confirm funds received after Xendit disbursement. Checks all jobs in WAITING_INDODAX status, compares Indodax balance with expected amounts, and updates job status to TRADING when confirmed. Admin only, typically run by cron job every hour.

Response

200Balance polling completed successfully
{
  "success": true,
  "message": "Balance polling completed successfully",
  "data": {
    "jobs_checked": 15,
    "balance_confirmed": 12,
    "still_waiting": 3,
    "timeout_exceeded": 0,
    "indodax_balance": {
      "idr": 50000000,
      "usdc": 250.5
    },
    "processing_time_ms": 2500
  },
  "timestamp": "2024-01-15T11:00:00Z"
}
500Balance polling failed
{
  "success": false,
  "error_code": "POLLING_FAILED",
  "message": "Failed to run balance polling"
}
curl -X POST https://api.brdz.link/api/indodax/conversion/poll-balance \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "x-api-key: YOUR_API_KEY"

Poll Job Balance

POST/api/indodax/conversion/poll-job/{job_id}

Poll Balance for Specific Job

Manually poll Indodax balance for a specific conversion job. Checks if disbursement amount has arrived at Indodax account and updates job status accordingly. Admin only operation.

Parameters

job_idnumberrequired

Conversion job ID to poll (path parameter)

Response

200Balance polling completed
{
  "success": true,
  "message": "Balance polling completed",
  "data": {
    "job_id": 123,
    "status": "TRADING",
    "balance_confirmed": true,
    "expected_amount": 1000000,
    "indodax_balance": 50000000,
    "confirmed_at": "2024-01-15T11:00:00Z"
  },
  "timestamp": "2024-01-15T11:00:01Z"
}
400Invalid job ID
{
  "success": false,
  "error_code": "INVALID_JOB_ID",
  "message": "Valid job ID is required"
}
500Job polling failed
{
  "success": false,
  "error_code": "JOB_POLL_FAILED",
  "message": "Failed to poll job balance"
}
curl -X POST https://api.brdz.link/api/indodax/conversion/poll-job/123 \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "x-api-key: YOUR_API_KEY"

Run Trade Execution

POST/api/indodax/conversion/run-trades

Run Trade Execution for All Ready Jobs

Execute USDC trades on Indodax for all jobs in TRADING status. Gets current USDC/IDR price, executes buy orders, polls trade status, and triggers USDC withdrawal to user wallets. Admin only, typically run by cron job.

Response

200Trade execution completed successfully
{
  "success": true,
  "message": "Trade execution completed successfully",
  "data": {
    "jobs_checked": 12,
    "trades_executed": 10,
    "trades_failed": 2,
    "total_idr_spent": 12000000,
    "total_usdc_received": 848.5,
    "withdrawals_initiated": 10,
    "processing_time_ms": 45000
  },
  "timestamp": "2024-01-15T12:00:00Z"
}
500Trade execution failed
{
  "success": false,
  "error_code": "TRADE_EXECUTION_FAILED",
  "message": "Failed to run trade execution"
}
curl -X POST https://api.brdz.link/api/indodax/conversion/run-trades \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "x-api-key: YOUR_API_KEY"

Execute Trade for Specific Job

POST/api/indodax/conversion/execute-trade/{job_id}

Execute Trade for Specific Job

Manually execute USDC trade for a specific conversion job. Gets current USDC/IDR price, executes buy order on Indodax, polls trade status until completion, and initiates USDC withdrawal. Admin only operation.

Parameters

job_idnumberrequired

Conversion job ID to execute trade for (path parameter)

Response

200Trade executed successfully
{
  "success": true,
  "message": "Trade executed successfully",
  "data": {
    "job_id": 123,
    "trade_id": "trade_9876543210",
    "status": "WITHDRAWING",
    "usdc_price": 14150,
    "idr_spent": 1000000,
    "usdc_received": 70.67,
    "trade_fee": 707,
    "withdrawal_id": "withdraw_1234567890",
    "wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
    "network": "erc20",
    "executed_at": "2024-01-15T12:00:00Z"
  },
  "timestamp": "2024-01-15T12:00:30Z"
}
400Invalid job ID or insufficient balance
{
  "success": false,
  "error_code": "INSUFFICIENT_INDODAX_BALANCE",
  "message": "Insufficient Indodax balance. Available: 500000, Required: 1000000"
}
500Trade execution failed
{
  "success": false,
  "error_code": "TRADE_EXECUTION_FAILED",
  "message": "Failed to execute trade"
}
curl -X POST https://api.brdz.link/api/indodax/conversion/execute-trade/123 \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "x-api-key: YOUR_API_KEY"

Get Conversion Status

GET/api/indodax/conversion/status/{job_id}

Get Conversion Job Status

Get detailed status and progress of a conversion job. Users can check their own jobs, admins can check any job. Returns complete job details including amounts, status transitions, and timestamps.

Parameters

job_idnumberrequired

Conversion job ID (path parameter)

Response

200Conversion status retrieved successfully
{
  "success": true,
  "message": "Conversion status retrieved successfully",
  "data": {
    "job_id": 123,
    "user_id": 456,
    "status": "COMPLETED",
    "batch_amount_idr": 1000000,
    "disbursement_id": "disb_1234567890",
    "disbursement_amount_idr": 1000000,
    "trade_id": "trade_9876543210",
    "usdc_received": 70.67,
    "trade_fee": 707,
    "withdrawal_id": "withdraw_1234567890",
    "wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
    "network": "erc20",
    "status_history": [
      {
        "status": "PENDING",
        "timestamp": "2024-01-15T02:00:00Z"
      },
      {
        "status": "WAITING_INDODAX",
        "timestamp": "2024-01-15T02:01:00Z"
      },
      {
        "status": "TRADING",
        "timestamp": "2024-01-15T11:00:00Z"
      },
      {
        "status": "WITHDRAWING",
        "timestamp": "2024-01-15T12:00:00Z"
      },
      {
        "status": "COMPLETED",
        "timestamp": "2024-01-15T12:30:00Z"
      }
    ],
    "created_at": "2024-01-15T02:00:00Z",
    "updated_at": "2024-01-15T12:30:00Z"
  },
  "timestamp": "2024-01-15T13:00:00Z"
}
400Invalid job ID
{
  "success": false,
  "error_code": "INVALID_JOB_ID",
  "message": "Valid job ID is required"
}
404Job not found
{
  "success": false,
  "error_code": "JOB_NOT_FOUND",
  "message": "Conversion job not found or access denied"
}
500Failed to get status
{
  "error": "Failed to get conversion status"
}
curl -X GET https://api.brdz.link/api/indodax/conversion/status/123 \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "x-api-key: YOUR_API_KEY"

🚀 Complete Conversion Workflows

Workflow 1: Setup Crypto Wallet for Conversion

// Complete wallet setup workflow
async function setupWalletForConversion(walletAddress, network) {
console.log('Setting up crypto wallet for conversions...');

try {
// Step 1: Check supported networks
const networksResponse = await fetch('https://api.brdz.link/api/indodax/crypto-wallet/networks', {
headers: {
'Authorization': 'Bearer YOUR_JWT_TOKEN',
'x-api-key': 'YOUR_API_KEY'
}
});

const networks = await networksResponse.json();
const supportedNetwork = networks.data.networks.find(
n => n.code.toUpperCase() === network.toUpperCase()
);

if (!supportedNetwork) {
throw new Error(\`Network \${network} is not supported\`);
}

console.log('✅ Network', supportedNetwork.name, 'is supported');
console.log('Example format:', supportedNetwork.example);

// Step 2: Save wallet
const saveResponse = await fetch('https://api.brdz.link/api/indodax/crypto-wallet/save', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_JWT_TOKEN',
'x-api-key': 'YOUR_API_KEY'
},
body: JSON.stringify({
wallet_address: walletAddress,
network: network,
set_as_primary: true
})
});

const wallet = await saveResponse.json();
console.log('✅ Wallet', wallet.data.action + ':', wallet.data.bw_id);
console.log('Network:', wallet.data.network);
console.log('Primary:', wallet.data.is_primary);

// Step 3: Verify primary wallet
const primaryResponse = await fetch('https://api.brdz.link/api/indodax/crypto-wallet/primary', {
headers: {
'Authorization': 'Bearer YOUR_JWT_TOKEN',
'x-api-key': 'YOUR_API_KEY'
}
});

const primary = await primaryResponse.json();
console.log('✅ Primary wallet confirmed:', primary.data.wallet_address);

// Step 4: Get all wallets
const listResponse = await fetch('https://api.brdz.link/api/indodax/crypto-wallet/list', {
headers: {
'Authorization': 'Bearer YOUR_JWT_TOKEN',
'x-api-key': 'YOUR_API_KEY'
}
});

const allWallets = await listResponse.json();
console.log('Total wallets:', allWallets.data.total);

return {
success: true,
wallet_id: wallet.data.bw_id,
network: wallet.data.network,
is_primary: wallet.data.is_primary
};

} catch (error) {
console.log('❌ Setup failed:', error.message);
return {
success: false,
error: error.message
};
}
}

// Usage
await setupWalletForConversion(
'0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
'ERC20'
);

Workflow 2: Track Conversion Job Progress

// Monitor conversion job from start to finish
async function trackConversionJob(jobId) {
console.log('Tracking conversion job', jobId);

const statusMap = {
'PENDING': 'Waiting for disbursement to Indodax',
'WAITING_INDODAX': 'Waiting for Indodax balance confirmation',
'TRADING': 'Executing USDC trade',
'WITHDRAWING': 'Withdrawing USDC to wallet',
'COMPLETED': 'Conversion completed successfully',
'FAILED': 'Conversion failed'
};

let lastStatus = '';
let attempts = 0;
const maxAttempts = 120; // 10 minutes with 5-second intervals

while (attempts < maxAttempts) {
try {
const response = await fetch(\`https://api.brdz.link/api/indodax/conversion/status/\${jobId}\`, {
headers: {
'Authorization': 'Bearer YOUR_JWT_TOKEN',
'x-api-key': 'YOUR_API_KEY'
}
});

const status = await response.json();
const currentStatus = status.data.status;

// Display status change
if (currentStatus !== lastStatus) {
const time = new Date().toLocaleTimeString();
console.log(\`[\${time}] Status: \${currentStatus}\`);
console.log('Description:', statusMap[currentStatus]);

// Display relevant details
if (status.data.disbursement_id) {
console.log('Disbursement ID:', status.data.disbursement_id);
}
if (status.data.trade_id) {
console.log('Trade ID:', status.data.trade_id);
console.log('USDC Received:', status.data.usdc_received);
}
if (status.data.withdrawal_id) {
console.log('Withdrawal ID:', status.data.withdrawal_id);
console.log('Network:', status.data.network);
}

lastStatus = currentStatus;
}

// Check if completed or failed
if (currentStatus === 'COMPLETED') {
console.log('✅ Conversion completed successfully!');
console.log('Amount:', status.data.batch_amount_idr.toLocaleString(), 'IDR');
console.log('USDC Received:', status.data.usdc_received);
console.log('Wallet:', status.data.wallet_address);

// Calculate total processing time
const created = new Date(status.data.created_at);
const completed = new Date(status.data.updated_at);
const totalMinutes = Math.round((completed - created) / 1000 / 60);
console.log('Total Processing Time:', totalMinutes, 'minutes');

return status.data;

} else if (currentStatus === 'FAILED') {
console.log('❌ Conversion failed');
return status.data;
}

// Wait 5 seconds before next check
await new Promise(resolve => setTimeout(resolve, 5000));
attempts++;

} catch (error) {
console.log('Error checking status:', error.message);
break;
}
}

console.log('⏰ Timeout: Job still in progress');
return null;
}

// Usage
await trackConversionJob(123);

Workflow 3: Admin Daily Conversion Process

// Complete admin workflow for daily conversions
async function performDailyConversionProcess() {
console.log('=== DAILY CONVERSION PROCESS ===');

const headers = {
'Authorization': 'Bearer YOUR_JWT_TOKEN',
'x-api-key': 'YOUR_API_KEY'
};

// Step 1: Check pending conversions
console.log('Step 1: Checking pending conversions...');
const pendingResponse = await fetch('https://api.brdz.link/api/indodax/conversion/pending', {
headers
});

const pending = await pendingResponse.json();
console.log('Total pending users:', pending.data.total);

const totalAmount = pending.data.users.reduce(
(sum, user) => sum + user.total_amount_idr,
0
);
console.log('Total pending amount:', totalAmount.toLocaleString(), 'IDR');

// Check for users without wallets
const noWallet = pending.data.users.filter(u => !u.has_primary_wallet);
if (noWallet.length > 0) {
console.log('⚠️ ', noWallet.length, 'users missing primary wallet');
}

// Step 2: Run daily conversion
console.log('Step 2: Running daily conversion...');
const conversionResponse = await fetch('https://api.brdz.link/api/indodax/conversion/run-daily', {
method: 'POST',
headers
});

const conversion = await conversionResponse.json();
console.log('✅ Jobs created:', conversion.data.jobs_created);
console.log('Disbursements initiated:', conversion.data.disbursements_initiated);
console.log('Processing time:', conversion.data.processing_time_ms, 'ms');

// Wait for balance confirmations
await new Promise(resolve => setTimeout(resolve, 2000));

// Step 3: Run balance polling
console.log('Step 3: Running balance polling...');
const pollingResponse = await fetch('https://api.brdz.link/api/indodax/conversion/poll-balance', {
method: 'POST',
headers
});

const polling = await pollingResponse.json();
console.log('Jobs checked:', polling.data.jobs_checked);
console.log('Balance confirmed:', polling.data.balance_confirmed);
console.log('Still waiting:', polling.data.still_waiting);

// Step 4: Run trade execution
if (polling.data.balance_confirmed > 0) {
console.log('Step 4: Running trade execution...');
const tradesResponse = await fetch('https://api.brdz.link/api/indodax/conversion/run-trades', {
method: 'POST',
headers
});

const trades = await tradesResponse.json();
console.log('Trades executed:', trades.data.trades_executed);
console.log('IDR spent:', trades.data.total_idr_spent.toLocaleString());
console.log('USDC received:', trades.data.total_usdc_received);
console.log('Withdrawals initiated:', trades.data.withdrawals_initiated);
}

console.log('=== PROCESS COMPLETE ===');

return {
pending: pending.data.total,
jobs_created: conversion.data.jobs_created,
balance_confirmed: polling.data.balance_confirmed,
trades_executed: trades?.data.trades_executed || 0
};
}

// Usage (run as cron job)
await performDailyConversionProcess();

Workflow 4: Manual Job Execution (Admin)

// Manually execute specific conversion job
async function manualJobExecution(jobId) {
console.log('=== MANUAL JOB EXECUTION:', jobId, '===');

const headers = {
'Authorization': 'Bearer YOUR_JWT_TOKEN',
'x-api-key': 'YOUR_API_KEY'
};

try {
// Step 1: Check current status
console.log('Step 1: Checking current job status...');
let statusResponse = await fetch(\`https://api.brdz.link/api/indodax/conversion/status/\${jobId}\`, {
headers
});

let status = await statusResponse.json();
console.log('Current Status:', status.data.status);
console.log('Amount:', status.data.batch_amount_idr.toLocaleString(), 'IDR');

// Step 2: Execute based on current status
if (status.data.status === 'PENDING') {
console.log('Step 2: Executing conversion job...');
const executionResponse = await fetch(\`https://api.brdz.link/api/indodax/conversion/execute/\${jobId}\`, {
method: 'POST',
headers
});

const execution = await executionResponse.json();
console.log('✅ Disbursement initiated:', execution.data.disbursement_id);
console.log('New Status:', execution.data.status);

// Wait for status update
await new Promise(resolve => setTimeout(resolve, 3000));
statusResponse = await fetch(\`https://api.brdz.link/api/indodax/conversion/status/\${jobId}\`, {
headers
});
status = await statusResponse.json();
}

// Step 3: Poll balance if waiting
if (status.data.status === 'WAITING_INDODAX') {
console.log('Step 3: Polling Indodax balance...');
const pollingResponse = await fetch(\`https://api.brdz.link/api/indodax/conversion/poll-job/\${jobId}\`, {
method: 'POST',
headers
});

const polling = await pollingResponse.json();

if (polling.data.balance_confirmed) {
console.log('✅ Balance confirmed at Indodax');
console.log('New Status:', polling.data.status);
} else {
console.log('⏳ Balance not yet confirmed');
console.log('Expected:', polling.data.expected_amount.toLocaleString(), 'IDR');
return;
}

// Wait for status update
await new Promise(resolve => setTimeout(resolve, 3000));
statusResponse = await fetch(\`https://api.brdz.link/api/indodax/conversion/status/\${jobId}\`, {
headers
});
status = await statusResponse.json();
}

// Step 4: Execute trade if ready
if (status.data.status === 'TRADING') {
console.log('Step 4: Executing USDC trade...');
const tradeResponse = await fetch(\`https://api.brdz.link/api/indodax/conversion/execute-trade/\${jobId}\`, {
method: 'POST',
headers
});

const trade = await tradeResponse.json();
console.log('✅ Trade executed:', trade.data.trade_id);
console.log('USDC Received:', trade.data.usdc_received);
console.log('Withdrawal initiated:', trade.data.withdrawal_id);
console.log('New Status:', trade.data.status);
}

// Step 5: Final status check
console.log('Step 5: Final status check...');
const finalStatusResponse = await fetch(\`https://api.brdz.link/api/indodax/conversion/status/\${jobId}\`, {
headers
});

const finalStatus = await finalStatusResponse.json();
console.log('Final Status:', finalStatus.data.status);

if (finalStatus.data.status === 'COMPLETED') {
console.log('✅ Job completed successfully!');
console.log('Summary:');
console.log(' IDR Amount:', finalStatus.data.batch_amount_idr.toLocaleString());
console.log(' USDC Received:', finalStatus.data.usdc_received);
console.log(' Trade Fee:', finalStatus.data.trade_fee, 'IDR');
console.log(' Wallet:', finalStatus.data.wallet_address);
console.log(' Network:', finalStatus.data.network);
} else {
console.log('Current Status:', finalStatus.data.status);
console.log('Continue monitoring job status');
}

return finalStatus.data;

} catch (error) {
console.log('❌ Manual execution failed:', error.message);
throw error;
}
}

// Usage
await manualJobExecution(123);

Workflow 5: User Conversion Monitoring

// Simple user workflow to monitor their conversion
async function monitorMyConversion(jobId) {
console.log('Monitoring your conversion...');

const response = await fetch(\`https://api.brdz.link/api/indodax/conversion/status/\${jobId}\`, {
headers: {
'Authorization': 'Bearer YOUR_JWT_TOKEN',
'x-api-key': 'YOUR_API_KEY'
}
});

const status = await response.json();

// Simple status display
const statusText = {
'PENDING': '⏳ Your conversion is being processed',
'WAITING_INDODAX': '⏳ Waiting for exchange confirmation',
'TRADING': '📊 Trading USDC on exchange',
'WITHDRAWING': '💸 Sending USDC to your wallet',
'COMPLETED': '✅ Conversion completed!',
'FAILED': '❌ Conversion failed'
};

console.log(statusText[status.data.status]);
console.log('Amount:', status.data.batch_amount_idr.toLocaleString(), 'IDR');

// Show relevant info based on status
if (status.data.status === 'COMPLETED') {
console.log('Your USDC has been sent!');
console.log('USDC Received:', status.data.usdc_received);
console.log('Wallet:', status.data.wallet_address);
console.log('Network:', status.data.network);
console.log('Check your wallet to confirm receipt.');
} else if (status.data.status === 'FAILED') {
console.log('Something went wrong with your conversion.');
console.log('Please contact support for assistance.');
} else {
console.log('Your conversion is in progress.');
console.log('You will be notified when it completes.');

// Estimated time remaining
const statusSteps = ['PENDING', 'WAITING_INDODAX', 'TRADING', 'WITHDRAWING'];
const currentStep = statusSteps.indexOf(status.data.status);
const totalSteps = statusSteps.length;
const progressPercent = Math.round((currentStep / totalSteps) * 100);

console.log('Progress:', progressPercent + '%');
console.log('Estimated completion: 2-12 hours');
}

return status.data;
}

// Usage
await monitorMyConversion(123);

🔐 Authentication & Authorization

All Indodax endpoints require authentication and specific role permissions:

// Authentication headers required for all requests
const headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_JWT_TOKEN', // Required for all endpoints
'x-api-key': 'YOUR_API_KEY' // Required for all endpoints
};

// User-level operations (user or admin role)
- Save/list/delete crypto wallets
- Get supported networks
- Get conversion status
- Start manual disbursement

// Admin-only operations (admin role required)
- Run daily conversion
- Execute conversion jobs
- Get pending conversions
- Run balance polling
- Execute trades

🌍 Supported Networks & Features

Blockchain Network Support

NetworkCodeAddress FormatExampleIndodax Code
Ethereum (ERC-20)ERC200x + 40 hex chars0x742d35Cc...erc20
Tron (TRC-20)TRC20T + 33 alphanumericTN3W4H6rK2...trc20
BSC (BEP-20)BEP200x + 40 hex chars0x742d35Cc...bep20
PolygonPOLYGON0x + 40 hex chars0x742d35Cc...polygon
SolanaSOLANA32-44 base58 chars7EcDhSYGx...solana

Conversion Features

  • Automated Daily Conversion: Scheduled conversion from IDR to USDC
  • Multi-stage Processing: PENDING → WAITING_INDODAX → TRADING → WITHDRAWING → COMPLETED
  • Balance Polling: Automatic confirmation of Indodax balance
  • Real-time Trading: Live USDC/IDR price fetching and trade execution
  • Automatic Withdrawal: USDC sent directly to user's crypto wallet
  • Job Tracking: Complete status monitoring and history

Processing Stages

  1. PENDING: Conversion job created, awaiting disbursement
  2. WAITING_INDODAX: Funds sent to Indodax, waiting for balance confirmation
  3. TRADING: Balance confirmed, executing USDC buy order
  4. WITHDRAWING: Trade completed, sending USDC to user wallet
  5. COMPLETED: USDC successfully delivered to wallet
  6. FAILED: Error occurred, manual review required

💡 Pro Tips for Indodax Integration

Wallet Management Best Practices

// Always verify primary wallet before conversions
async function ensurePrimaryWallet() {
const response = await fetch('https://api.brdz.link/api/indodax/crypto-wallet/primary', {
headers: {
'Authorization': 'Bearer YOUR_JWT_TOKEN',
'x-api-key': 'YOUR_API_KEY'
}
});

const result = await response.json();

if (result.success) {
console.log('✅ Primary wallet set:', result.data.wallet_address);
return true;
} else {
console.log('❌ No primary wallet - user must add wallet first');
return false;
}
}

Conversion Job Monitoring

// Poll job status with exponential backoff
async function smartJobMonitoring(jobId) {
const delays = [5, 10, 30, 60, 300]; // seconds
let delayIndex = 0;

while (true) {
const response = await fetch(\`https://api.brdz.link/api/indodax/conversion/status/\${jobId}\`, {
headers: {
'Authorization': 'Bearer YOUR_JWT_TOKEN',
'x-api-key': 'YOUR_API_KEY'
}
});

const status = await response.json();

if (['COMPLETED', 'FAILED'].includes(status.data.status)) {
return status.data;
}

const delay = delays[Math.min(delayIndex, delays.length - 1)];
console.log('Status:', status.data.status, '- checking again in', delay, 's');

await new Promise(resolve => setTimeout(resolve, delay * 1000));
delayIndex++;
}
}

Error Handling

// Robust error handling for conversions
class IndodaxErrorHandler {
static async safeConversion(jobId) {
try {
const response = await fetch(\`https://api.brdz.link/api/indodax/conversion/execute/\${jobId}\`, {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_JWT_TOKEN',
'x-api-key': 'YOUR_API_KEY'
}
});

return await response.json();
} catch (error) {
return this.handleConversionError(error);
}
}

static handleConversionError(error) {
const errorMap = {
'NO_PRIMARY_WALLET': {
message: 'User has not set up crypto wallet',
action: 'Prompt user to add wallet via saveCryptoWallet',
retry: false
},
'INVALID_WALLET_FORMAT': {
message: 'Wallet address format is invalid',
action: 'Verify wallet address format for selected network',
retry: false
},
'RECONCILIATION_FAILED': {
message: 'Balance reconciliation failed',
action: 'Admin must review and resolve reconciliation',
retry: false
},
'INSUFFICIENT_INDODAX_BALANCE': {
message: 'Indodax account has insufficient balance',
action: 'Top up Indodax account before trading',
retry: false
},
'TRADE_EXECUTION_FAILED': {
message: 'Trade execution failed on Indodax',
action: 'Check Indodax API status and retry',
retry: true
}
};

const errorCode = error.error_code || 'UNKNOWN_ERROR';
const errorInfo = errorMap[errorCode] || {
message: error.message,
action: 'Review error logs and contact support',
retry: false
};

console.log('Error:', errorInfo.message);
console.log('Action:', errorInfo.action);

return {
success: false,
error_code: errorCode,
...errorInfo
};
}
}

// Usage
const result = await IndodaxErrorHandler.safeConversion(123);
if (!result.success && result.retry) {
console.log('Safe to retry this operation');
}

Cron Job Schedule

// Recommended cron schedule for automated operations
const cron = require('node-cron');

const headers = {
'Authorization': 'Bearer YOUR_JWT_TOKEN',
'x-api-key': 'YOUR_API_KEY'
};

// Daily conversion at 2 AM
cron.schedule('0 2 * * *', async () => {
console.log('Running daily conversion...');
await fetch('https://api.brdz.link/api/indodax/conversion/run-daily', {
method: 'POST',
headers
});
});

// Balance polling every hour
cron.schedule('0 * * * *', async () => {
console.log('Running balance polling...');
await fetch('https://api.brdz.link/api/indodax/conversion/poll-balance', {
method: 'POST',
headers
});
});

// Trade execution every 2 hours
cron.schedule('0 */2 * * *', async () => {
console.log('Running trade execution...');
await fetch('https://api.brdz.link/api/indodax/conversion/run-trades', {
method: 'POST',
headers
});
});

🔄 Complete Conversion Flow Diagram

┌─────────────────────────────────────────────────────────────────┐
│ INDODAX CONVERSION FLOW │
└─────────────────────────────────────────────────────────────────┘

USER SETUP:
┌──────────────┐
│ User adds │
│ crypto wallet│
│ (ERC20, etc) │
└──────┬───────┘

v
┌──────────────┐
│ Set as │
│ primary │
│ wallet │
└──────┬───────┘


[Ready for Conversion]

═══════════════════════════════════════════════════════════════

AUTOMATED CONVERSION (Admin/Cron):

STEP 1: JOB CREATION
┌──────────────┐
│ Daily Cron │
│ (2 AM) │
└──────┬───────┘

v
┌──────────────────────────┐
│ Get eligible users │
│ with IDR balance │
└──────────┬───────────────┘

v
┌──────────────────────────┐
│ Create conversion_jobs │
│ Status: PENDING │
└──────────┬───────────────┘


STEP 2: DISBURSEMENT
┌──────────────────────────┐
│ Pre-disbursement │
│ reconciliation │
└──────────┬───────────────┘

v [PASSED]
┌──────────────────────────┐
│ Xendit disburse IDR │
│ to Indodax VA │
└──────────┬───────────────┘

v
┌──────────────────────────┐
│ Update Status: │
│ WAITING_INDODAX │
└──────────┬───────────────┘


STEP 3: BALANCE POLLING (Every hour)
┌──────────────────────────┐
│ Check Indodax balance │
│ via API │
└──────────┬───────────────┘

v [CONFIRMED]
┌──────────────────────────┐
│ Update Status: │
│ TRADING │
└──────────┬───────────────┘


STEP 4: TRADE EXECUTION (Every 2 hours)
┌──────────────────────────┐
│ Get USDC/IDR price │
└──────────┬───────────────┘

v
┌──────────────────────────┐
│ Execute buy order │
│ IDR → USDC │
└──────────┬───────────────┘

v
┌──────────────────────────┐
│ Poll trade status │
│ (10s interval, 5min max) │
└──────────┬───────────────┘

v [COMPLETED]
┌──────────────────────────┐
│ Update Status: │
│ WITHDRAWING │
└──────────┬───────────────┘


STEP 5: WITHDRAWAL
┌──────────────────────────┐
│ Indodax withdrawCoin() │
│ to user's wallet │
└──────────┬───────────────┘

v
┌──────────────────────────┐
│ Wait for Indodax │
│ withdrawal callback │
└──────────┬───────────────┘

v [SUCCESS]
┌──────────────────────────┐
│ Update Status: │
│ COMPLETED │
└──────────┬───────────────┘


[✅ DONE]

═══════════════════════════════════════════════════════════════

USER MONITORING:
┌──────────────────────────┐
│ User checks job status │
│ via API │
└──────────┬───────────────┘

v
┌──────────────────────────┐
│ Get status, USDC amount, │
│ wallet address, network │
└──────────────────────────┘

═══════════════════════════════════════════════════════════════

FAILURE HANDLING:
┌──────────────────────────┐
│ Any stage fails │
└──────────┬───────────────┘

v
┌──────────────────────────┐
│ Update Status: FAILED │
└──────────┬───────────────┘

v
┌──────────────────────────┐
│ Admin notification │
│ Manual review required │
└──────────────────────────┘

📊 Conversion Statistics & Monitoring

// Get conversion statistics for monitoring
async function getConversionStats(startDate, endDate) {
const headers = {
'Authorization': 'Bearer YOUR_JWT_TOKEN',
'x-api-key': 'YOUR_API_KEY'
};

// Get all conversions in date range
const response = await fetch('https://api.brdz.link/api/indodax/conversion/pending', {
headers
});

const pending = await response.json();

// Calculate metrics
const stats = {
total_users: pending.data.total,
total_pending_idr: pending.data.users.reduce(
(sum, u) => sum + u.total_amount_idr,
0
),
users_with_wallets: pending.data.users.filter(
u => u.has_primary_wallet
).length,
users_without_wallets: pending.data.users.filter(
u => !u.has_primary_wallet
).length,
network_distribution: {}
};

// Calculate network distribution
pending.data.users.forEach(user => {
if (user.has_primary_wallet) {
const network = user.wallet_network;
stats.network_distribution[network] =
(stats.network_distribution[network] || 0) + 1;
}
});

return stats;
}

// Usage
const stats = await getConversionStats('2024-01-01', '2024-01-31');

console.log('Conversion Statistics:');
console.log('Total Users:', stats.total_users);
console.log('Total Pending:', stats.total_pending_idr.toLocaleString(), 'IDR');
console.log('With Wallets:', stats.users_with_wallets);
console.log('Without Wallets:', stats.users_without_wallets);
console.log('Network Distribution:');
Object.entries(stats.network_distribution).forEach(([network, count]) => {
console.log(\` \${network}: \${count}\`);
});

⚠️ Important Notes & Warnings

Testing Environment

The current Indodax implementation uses pooled custody which may not be compliant with POJK 27/2024 (Indonesia's crypto regulation). This is for TESTING PURPOSES ONLY. Production deployment requires proper licensing or partnership with a licensed DFA (Digital Financial Asset) Trader.

Wallet Setup Requirement

Users must add and verify their crypto wallet before any conversion can be processed. Always check for primary wallet existence before initiating conversions.

Conversion Timing

Complete conversion process typically takes 2-12 hours depending on:

  • Xendit disbursement processing time (1-2 hours)
  • Indodax balance confirmation (1 hour polling interval)
  • Trade execution timing (2 hour execution interval)
  • USDC withdrawal processing (varies by network)
Balance Monitoring

Admin should monitor Indodax account balance to ensure sufficient IDR is available for trading. Set up alerts when balance falls below threshold (e.g., 50M IDR).

Reconciliation

Always run pre-disbursement reconciliation to prevent balance discrepancies. Any reconciliation failures require manual admin resolution before proceeding.


🔧 Environment Configuration

Required Environment Variables

# Indodax API Credentials
INDODAX_PUBLIC_API_URL=https://indodax.com/api
INDODAX_TRADE_API_URL=https://indodax.com/tapi
INDODAX_API_KEY=your_indodax_api_key
INDODAX_API_SECRET=your_indodax_api_secret

# Indodax Virtual Account (via Xendit)
INDODAX_XENDIT_VA_NUMBER=8888012345678901
INDODAX_XENDIT_VA_BANK=PERMATA
INDODAX_XENDIT_VA_NAME=ANANTLA TECH

# Polling Configuration
INDODAX_POLLING_INTERVAL_MS=3600000 # 1 hour
INDODAX_POLLING_MAX_ATTEMPTS=24 # 24 hours timeout
INDODAX_TRADE_POLLING_INTERVAL_MS=10000 # 10 seconds
INDODAX_TRADE_MAX_WAIT_MS=300000 # 5 minutes

# Xendit Configuration (required for disbursement)
XENDIT_MODE=test # or 'live'
API_KEY_XENDIT_TEST=your_xendit_test_key
API_KEY_XENDIT_LIVE=your_xendit_live_key
XENDIT_BASE_URL=https://api.xendit.co

🚨 Troubleshooting Guide

Common Issues & Solutions

Issue 1: "No primary wallet found"

Cause: User hasn't set up crypto wallet
Solution:

// Prompt user to add wallet
await fetch('https://api.brdz.link/api/indodax/crypto-wallet/save', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_JWT_TOKEN',
'x-api-key': 'YOUR_API_KEY'
},
body: JSON.stringify({
wallet_address: '0x...',
network: 'ERC20',
set_as_primary: true
})
});

Issue 2: "Invalid wallet format"

Cause: Wallet address doesn't match network format
Solution:

// Check supported networks first
const response = await fetch('https://api.brdz.link/api/indodax/crypto-wallet/networks', {
headers: {
'Authorization': 'Bearer YOUR_JWT_TOKEN',
'x-api-key': 'YOUR_API_KEY'
}
});

const networks = await response.json();
// Validate address format against network.example

Issue 3: "Reconciliation failed"

Cause: Balance mismatch between ledger and Xendit
Solution: Admin must review and manually resolve reconciliation before proceeding

Issue 4: "Job stuck in WAITING_INDODAX"

Cause: Funds not yet arrived at Indodax or balance polling not running
Solution:

// Manually poll job
await fetch(\`https://api.brdz.link/api/indodax/conversion/poll-job/\${job_id}\`, {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_JWT_TOKEN',
'x-api-key': 'YOUR_API_KEY'
}
});

// Check balance polling cron is running

Issue 5: "Trade execution failed"

Cause: Indodax API error or insufficient balance
Solution:

  • Check Indodax API status
  • Verify Indodax account has sufficient IDR
  • Check trade execution logs for specific error

Issue 6: "Withdrawal stuck in WITHDRAWING"

Cause: Waiting for Indodax withdrawal callback
Solution: Monitor Indodax withdrawal status, callback may be delayed


📚 API Reference Summary

Quick Reference Table

EndpointMethodAuthDescription
/crypto-wallet/savePOSTUser/AdminSave crypto wallet
/crypto-wallet/listGETUser/AdminList all wallets
/crypto-wallet/primaryGETUser/AdminGet primary wallet
/crypto-wallet/deletePOSTUser/AdminDelete wallet
/crypto-wallet/networksGETUser/AdminGet supported networks
/disbursement/startPOSTUser/AdminStart manual disbursement
/conversion/run-dailyPOSTAdminRun daily conversion
/conversion/execute/:job_idPOSTAdminExecute specific job
/conversion/pendingGETAdminGet pending conversions
/conversion/poll-balancePOSTAdminRun balance polling
/conversion/poll-job/:job_idPOSTAdminPoll specific job
/conversion/run-tradesPOSTAdminRun trade execution
/conversion/execute-trade/:job_idPOSTAdminExecute specific trade
/conversion/status/:job_idGETUser/AdminGet job status

💬 Support & Feedback

For technical support, API issues, or feature requests related to Indodax integration:

  • Check the documentation above
  • Review error codes and troubleshooting guide
  • Contact Anantla technical support
  • Report bugs via support channels