API Reference

Overview

The Virtual Accounts API allows partners to retrieve virtual account transaction data through a secure, signed request mechanism. All requests must be authenticated using RSA-SHA256 digital signatures.

Base URL

https://api.moonpay.com

Authentication

RSA-SHA256 Digital Signatures

All API requests must be signed using RSA-SHA256 digital signatures. The signature is generated by signing the complete request payload (including path and query parameters) with your private key.

Required Headers

HeaderDescription
x-signatureBase64-encoded RSA-SHA256 signature of the request payload
Content-Typeapplication/json

Signature Generation Process

  1. Create the payload: Concatenate the request path with query parameters

    Format: {path}?{queryString}
    Example: /v1/virtual-accounts/transactions?apiKey=pk_test_xxx&timestamp=1234567890
    
  2. Generate signature: Use RSA-SHA256 to sign the payload with your private key

    const sign = crypto.createSign('SHA256');
    sign.update(payload);
    const signature = sign.sign(privateKey, 'base64');
    
  3. Include signature: Add the signature to the x-signature header

Get Virtual Account Details

Retrieves virtual account details. Can be filtered by customer ID and/or wallet address.

Request

GET /v1/virtual-accounts?apiKey={publishableApiKey}&timestamp={timestamp}&customerId={customerId}&walletAddress={walletAddress}

Query Parameters

ParameterTypeRequiredDescription
apiKeystringYesYour publishable API key (starts with pk_test_ for sandbox or pk_live_ for production)
timestampnumberYesUnix timestamp in milliseconds for request validation
customerIdstringNoOptional filter by customer ID
walletAddressstringNoOptional filter by wallet address
virtualAccountIdstringNoOptional filter by virtual account id

Headers

HeaderTypeRequiredDescription
x-signaturestringYesRSA-SHA256 signature of the request payload
Content-TypestringYesMust be application/json

Example Request

async function getVirtualAccountDetails(customerId = null, walletAddress = null) {
  // Generate timestamp
  const timestamp = Date.now();
  
  // Build query string with optional parameters
  let queryParams = [`apiKey=${PUBLISHABLE_API_KEY}`, `timestamp=${timestamp}`];
  if (customerId) queryParams.push(`customerId=${customerId}`);
  if (walletAddress) queryParams.push(`walletAddress=${walletAddress}`);
  
  // Build payload for signature
  const path = '/v1/virtual-accounts';
  const queryString = queryParams.join('&');
  const payload = `${path}?${queryString}`;
  
  // Create RSA-SHA256 signature
  const sign = crypto.createSign('SHA256');
  sign.update(payload);
  const signature = sign.sign(PRIVATE_KEY, 'base64');
  
  // Make the request
  const url = `${SERVER_URL}${payload}`;
  const response = await fetch(url, {
    method: 'GET',
    headers: {
      'x-signature': signature,
      'Content-Type': 'application/json',
    },
  });
  
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${await response.text()}`);
  }
  
  return await response.json();
}

// Usage examples:
// Get all virtual accounts
await getVirtualAccountDetails();

// Filter by customer ID only
await getVirtualAccountDetails('customerId123');

// Filter by wallet address only
await getVirtualAccountDetails(null, '0x1234567890123456789012345678901234567890');

// Filter by both customer ID and wallet address
await getVirtualAccountDetails('customerId123', '0x1234567890123456789012345678901234567890');

Success Response (200 OK)

[
    {
        "id": "1234567890",
        "type": "auto_on_ramp",
        "destination": {
            "wallet": {
                "walletAddress": "0x1234576867890123456789012345678901234567",
                "walletAddressTag": "usdc_sol"
            },
            "network": {
                "code": "solana",
                "name": "Solana",
                "icon": "https://static.moonpay.com/widget/currencies/usdc.svg",
                "isAddressCaseSensitive": false,
                "isEvmCompatible": true,
                "addressRegex": "0x[a-fA-F0-9]{40}",
                "supportsAddressTag": true,
                "testnetWalletExplorerLink": "https://solana.com/testnet",
                "mainnetWalletExplorerLink": "https://solana.com/mainnet",
                "testnetTxnExplorerLink": "https://solana.com/testnet",
                "mainnetTxnExplorerLink": "https://solana.com/mainnet",
                "addressTagRegex": "0x[a-fA-F0-9]{40}",
                "createdAt": "2025-06-10T11:45:25.202Z",
                "updatedAt": "2025-06-10T11:45:25.202Z"
            }
        },
        "sourceCurrency": {
            "id": "123",
            "code": "USD",
            "name": "US Dollar",
            "precision": 2,
            "icon": "",
            "maxAmount": 1000000,
            "minAmount": 1,
            "maxBuyAmount": 1000000,
            "minBuyAmount": 1,
            "accountBuyMinAmount": 1,
            "isSellSupported": true,
            "deletedAt": null,
            "type": "fiat",
            "decimals": 2,
            "spreadPercentage": 0,
            "isUtxoCompatible": false,
            "supportedAcquirers": [],
            "createdAt": "2025-06-10T11:45:25.202Z",
            "updatedAt": "2025-06-10T11:45:25.202Z"
        },
        "destinationCurrency": {
            "id": "123",
            "code": "usdc_sol",
            "name": "USD Coin (Solana)",
            "type": "crypto",
            "precision": 2,
            "icon": "",
            "maxAmount": 1000000,
            "minAmount": 1,
            "maxBuyAmount": 1000000,
            "minBuyAmount": 1
        },
        "customerId": "123",
        "organizationId": "123",
        "createdAt": "2025-06-10T11:45:25.202Z",
        "updatedAt": "2025-06-10T11:45:25.202Z",
        "source": {
            "iban": "DE89370400440532013000",
            "bic": "DEUTDEDB123",
            "accountNumber": "1234567890",
            "sortCode": "1234567890",
            "bankName": "Deutsche Bank",
            "bankAddress": "123 Main St, Anytown, USA",
            "accountHolderName": "John Doe",
            "accountHolderAddress": "123 Main St, Anytown, USA"
        }
    },
    {
        "id": "1234567890",
        "type": "auto_on_ramp",
        "destination": {
            "wallet": {
                "walletAddress": "0x1234576867890123456789012345678901234567",
                "walletAddressTag": "usdc_sol"
            },
            "network": {
                "code": "solana",
                "name": "Solana",
                "icon": "https://static.moonpay.com/widget/currencies/usdc.svg",
                "isAddressCaseSensitive": false,
                "isEvmCompatible": true,
                "addressRegex": "0x[a-fA-F0-9]{40}",
                "supportsAddressTag": true,
                "testnetWalletExplorerLink": "https://solana.com/testnet",
                "mainnetWalletExplorerLink": "https://solana.com/mainnet",
                "testnetTxnExplorerLink": "https://solana.com/testnet",
                "mainnetTxnExplorerLink": "https://solana.com/mainnet",
                "addressTagRegex": "0x[a-fA-F0-9]{40}",
                "createdAt": "2025-06-10T11:45:25.202Z",
                "updatedAt": "2025-06-10T11:45:25.202Z"
            }
        },
        "sourceCurrency": {
            "id": "123",
            "code": "USD",
            "name": "US Dollar",
            "precision": 2,
            "icon": "",
            "maxAmount": 1000000,
            "minAmount": 1,
            "maxBuyAmount": 1000000,
            "minBuyAmount": 1,
            "accountBuyMinAmount": 1,
            "isSellSupported": true,
            "deletedAt": null,
            "type": "fiat",
            "decimals": 2,
            "spreadPercentage": 0,
            "isUtxoCompatible": false,
            "supportedAcquirers": [],
            "createdAt": "2025-06-10T11:45:25.202Z",
            "updatedAt": "2025-06-10T11:45:25.202Z"
        },
        "destinationCurrency": {
            "id": "123",
            "code": "usdc_sol",
            "name": "USD Coin (Solana)",
            "type": "crypto",
            "precision": 2,
            "icon": "",
            "maxAmount": 1000000,
            "minAmount": 1,
            "maxBuyAmount": 1000000,
            "minBuyAmount": 1
        },
        "customerId": "123",
        "organizationId": "123",
        "createdAt": "2025-06-10T11:45:25.202Z",
        "updatedAt": "2025-06-10T11:45:25.202Z",
        "source": {
            "iban": "DE89370400440532013000",
            "bic": "DEUTDEDB123",
            "accountNumber": "1234567890",
            "sortCode": "1234567890",
            "bankName": "Deutsche Bank",
            "bankAddress": "123 Main St, Anytown, USA",
            "accountName": "John Doe",
            "accountAddress": "123 Main St, Anytown, USA"
        }
    }
]

Response Field Descriptions

FieldTypeDescription
idstringUnique virtual account identifier
typestringCurrent virtual account type (auto_on_ramp, auto_off_ramp)
statestringCurrent virtual account state (pending, authorized, depositAccountAdded, approved, rejected, cancelled)
sourceCurrencyobjectSource currency details including code, name, precision, and limits
destinationCurrencyobjectDestination currency details
sourceobjectSource account information (name, IBAN, address)
destinationobjectDestination wallet and network information (wallet address, wallet address tag, network code, network name, network icon, network is address case sensitive, network is EVM compatible, network address regex, network supports address tag, network testnet wallet explorer link, network mainnet wallet explorer link, network testnet txn explorer link, network mainnet txn explorer link, network address tag regex, network created at, network updated at)
createdAtstringISO 8601 timestamp of virtual account creation

Virtual Account Types

TypeDescription
auto_on_rampVirtual account is receiving FIAT and sending Crypto
auto_off_rampVirtual account is receiving Crypto and sending FIAT

Virtual Account States

StateDescription
pendingVirtual account is being created
authorizedVirtual account has been authorized by the customer
depositAccountAddedVirtual account has been added to the deposit account
approvedVirtual account has been approved
rejectedVirtual account has been rejected
cancelledVirtual account has been cancelled

Get Virtual Account Transactions

Retrieves virtual account transaction data for the authenticated partner.

Request

GET /v1/virtual-accounts/transactions?apiKey={publishableApiKey}&timestamp={timestamp}&virtualAccountId={virtualAccountId}&customerId={customerId}

Query Parameters

ParameterTypeRequiredDescription
apiKeystringYesYour publishable API key (starts with pk_test_ for sandbox or pk_live_ for production)
timestampnumberYesUnix timestamp in milliseconds for request validation
virtualAccountIdstringNoOptional filter the transaction list by virtual account id
customerIdstringNoOptional filter the transaction list by customer id

Headers

HeaderTypeRequiredDescription
x-signaturestringYesRSA-SHA256 signature of the request payload
Content-TypestringYesMust be application/json

Example Request

const crypto = require('crypto');
const fetch = require('node-fetch');

const SERVER_URL = 'https://api.moonpay.com';
const PUBLISHABLE_API_KEY = '[Your Publishable Api Key]';
const PRIVATE_KEY = `-----BEGIN PRIVATE KEY-----
[Your Private Key Here]
-----END PRIVATE KEY-----`;

async function getVirtualAccountTransactions(virtualAccountId) {
  // Generate timestamp
  const timestamp = Date.now();
  
  // Build payload for signature
  const path = '/v1/virtual-accounts/transactions';
  const queryString = `apiKey=${PUBLISHABLE_API_KEY}&timestamp=${timestamp}&virtualAccountId=${virtualAccountId}`;
  const payload = `${path}?${queryString}`;
  
  // Create RSA-SHA256 signature
  const sign = crypto.createSign('SHA256');
  sign.update(payload);
  const signature = sign.sign(PRIVATE_KEY, 'base64');
  
  // Make the request
  const url = `${SERVER_URL}${payload}`;
  const response = await fetch(url, {
    method: 'GET',
    headers: {
      'x-signature': signature,
      'Content-Type': 'application/json',
    },
  });
  
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${await response.text()}`);
  }
  
  return await response.json();
}

Success Response (200 OK)

{
  "transactions": [
    {
      "id": "1234567890",
      "state": "completed",
      "sourceAmount": "100",
      "sourceCurrency": {
        "id": "123",
        "code": "USD",
        "name": "US Dollar",
        "precision": 2,
        "icon": "",
        "maxBuyAmount": 1000000,
        "minBuyAmount": 1,
        "accountBuyMinAmount": 1,
        "isSellSupported": true,
        "maxAmount": 1000000,
        "minAmount": 1,
        "createdAt": "2025-06-10T11:47:27.582Z",
        "updatedAt": "2025-06-10T11:47:27.582Z"
      },
      "destinationAmount": "100",
      "destinationCurrency": {
        "id": "123",
        "code": "usdc_sol",
        "name": "USD Coin (Solana)",
        "precision": 2,
        "icon": "",
        "maxBuyAmount": 1000000,
        "minBuyAmount": 1,
        "accountBuyMinAmount": 1,
        "isSellSupported": true,
        "maxAmount": 1000000,
        "minAmount": 1,
        "createdAt": "2025-06-10T11:47:27.582Z",
        "updatedAt": "2025-06-10T11:47:27.582Z"
      },
      "source": {
        "accountName": "John Doe",
        "iban": "DE89370400440532013000",
        "accountAddress": "123 Main St, Anytown, USA"
      },
      "destination": {
        "wallet": {
          "walletAddress": "0x1234576867890123456789012345678901234567",
          "walletAddressTag": "Solana"
        },
        "network": {
          "code": "solana",
          "name": "Solana",
          "icon": "https://static.moonpay.com/widget/currencies/usdc.svg",
          "isAddressCaseSensitive": false,
          "isEvmCompatible": true,
          "addressRegex": "0x[a-fA-F0-9]{40}",
          "supportsAddressTag": true,
          "testnetWalletExplorerLink": "https://solana.com/testnet",
          "mainnetWalletExplorerLink": "https://solana.com/mainnet",
          "testnetTxnExplorerLink": "https://solana.com/testnet",
          "mainnetTxnExplorerLink": "https://solana.com/mainnet",
          "addressTagRegex": "0x[a-fA-F0-9]{40}",
          "createdAt": "2025-06-10T11:47:27.582Z",
          "updatedAt": "2025-06-10T11:47:27.582Z"
        }
      },
      "createdAt": "2025-06-10T11:47:27.582Z"
    }
  ]
}

Response Field Descriptions

FieldTypeDescription
idstringUnique transaction identifier
statestringCurrent transaction state (pending, completed, failed)
sourceAmountstringAmount in source currency
sourceCurrencyobjectSource currency details including code, name, precision, and limits
destinationAmountstringAmount in destination currency
destinationCurrencyobjectDestination currency details
sourceobjectSource account information (name, IBAN, address)
destinationobjectDestination wallet and network information
createdAtstringISO 8601 timestamp of transaction creation

Transaction States

StateDescription
pendingTransaction is being processed
completedTransaction has been successfully completed
failedTransaction has failed

Errors

Error responses

400 Bad Request

{
  "success": false,
  "error": {
    "code": "INVALID_REQUEST",
    "message": "Missing required parameter: apiKey"
  }
}

401 Unauthorized

{
  "success": false,
  "error": {
    "code": "INVALID_SIGNATURE",
    "message": "Request signature verification failed"
  }
}

403 Forbidden

{
  "success": false,
  "error": {
    "code": "INVALID_API_KEY",
    "message": "The provided API key is invalid or inactive"
  }
}

429 Too Many Requests

{
  "success": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Rate limit exceeded. Please try again later."
  }
}

500 Internal Server Error

{
  "success": false,
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "An internal server error occurred"
  }
}

Error Handling Best Practices

  1. Always check response status codes before processing response data
  2. Implement retry logic for transient errors (5xx status codes)
  3. Log signature generation details for debugging authentication issues
  4. Handle rate limiting by implementing exponential backoff

Security Considerations

Private Key Management

  • Never expose your private key in client-side code or public repositories
  • Store private keys securely using environment variables or secure key management systems
  • Rotate private keys regularly for enhanced security

Request Validation

  • Timestamps are validated to prevent replay attacks
  • Signatures are verified against your registered public key
  • All requests must be made over HTTPS in production

Rate Limiting

  • API requests are rate-limited per API key
  • Current limit: 100 requests per minute per API key
  • Exceeding rate limits will result in HTTP 429 responses

Testing

Sandbox Environment

Use the following for testing:

  • API Key: Test keys start with pk_test_

Sample Test Data

The sandbox environment includes sample virtual account transactions for testing integration.