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/v1/virtual-accounts

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/onramp?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}&virtualAccountId={virtualAccountId}

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
walletAddressstringNoOptional filter by wallet address
customerIdstringNoOptional filter by customer ID
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, virtualAccountId = 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(`virtualAccountId=${virtualAccountId}`);
  
  // 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 virtual account ID
await getVirtualAccountDetails(null, 'virtualAccount123');

// Filter by both customer ID and virtual account ID
await getVirtualAccountDetails('customerId123', 'virtualAccount123');

Success Response (200 OK)

[
    {
        "id": "1234567890",
        "type": "auto_on_ramp",
        "destination": {
            "wallet": {
                "walletAddress": "0x1234576867890123456789012345678901234567",
                "walletAddressTag": null
            },
            "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",
        }
    }
]

Response Field Descriptions

FieldTypeDescription
idstringUnique virtual account identifier
typestringCurrent virtual account type (auto_on_ramp, auto_off_ramp)
statestringCurrent virtual account state (pending, completed, failed)
sourceCurrencyobjectSource currency details, including code, name, precision, and limits (Fiat for auto_on_ramp and Crypto for auto_off_ramp)
destinationCurrencyobjectDestination currency details (Crypto for auto_on_ramp and Fiat for auto_off_ramp)
sourceobjectSource account information: Bank account details for auto_on_ramp: name, IBAN, and address.
destinationobjectDestination wallet and network information (wallet and network)
createdAtstringISO 8601 timestamp of virtual account creation
updatedAtstringISO 8601 timestamp of virtual account last update

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.
completedVirtual account is ready to use by customer
failedVirtual account creation failed due to some reason.

Get Virtual Account OnRamp Transactions

Retrieves virtual account on-ramp transaction data for the authenticated partner (For VirtualAccount with type = auto_on_ramp)

Request

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

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
virtualAccountIdstringYesVirtual account ID to filter transactions by
pageSizenumberYesNumber of transactions per page
cursorstringNoCursor value from the previous page response

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/onramp';
  const queryString = `apiKey=${PUBLISHABLE_API_KEY}&timestamp=${timestamp}&virtualAccountId=${virtualAccountId}&pageSize=50`;
  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)

{
  "nextCursor": "12345",
  "transactions": [
    {
      "id": "1234567890",
      "status": "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": null
        },
        "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
nextCursorstringUnique string to fetch next page
transactionsArrayTransactions array

Transaction Field Descriptions

FieldTypeDescription
idstringUnique transaction identifier
statusstringCurrent transaction state (Pending, PayoutPending, Payout, PayoutCompleted, Completed, Failed, InAmlReview, AmlRejected, AmountRejected)
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

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.