Skip to main content
External accounts are bank accounts, cryptocurrency wallets, or payment destinations outside Grid where you can send funds. Grid supports two types:
  • Customer external accounts - Scoped to individual customers, used for withdrawals and customer-specific payouts
  • Platform external accounts - Scoped to your platform, used for platform-wide operations like receiving funds from external sources
Customer external accounts often require some basic beneficiary information for compliance. Platform accounts are managed at the organization level.

Create external accounts by region or wallet

  • United States
  • Mexico
  • Brazil
  • Europe
  • India
  • Cryptocurrency
ACH, Wire, RTP
cURL
curl -X POST 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts' \
  -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' \
  -H 'Content-Type: application/json' \
  -d '{
    "currency": "USD",
    "platformAccountId": "user_123_primary_bank",
    "accountInfo": {
      "accountType": "US_ACCOUNT",
      "accountNumber": "123456789",
      "routingNumber": "021000021",
      "accountCategory": "CHECKING",
      "bankName": "Chase Bank",
      "beneficiary": {
        "beneficiaryType": "INDIVIDUAL",
        "fullName": "John Doe",
        "birthDate": "1990-01-15",
        "nationality": "US",
        "address": {
          "line1": "123 Main Street",
          "city": "San Francisco",
          "state": "CA",
          "postalCode": "94105",
          "country": "US"
        }
      }
    }
  }'
Category must be CHECKING or SAVINGS. Routing number must be 9 digits.
Use platformAccountId to tie your internal id with the external account.
Sample Response:
{
  "id": "ExternalAccount:e85dcbd6-dced-4ec4-b756-3c3a9ea3d965",
  "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001",
  "status": "ACTIVE",
  "currency": "USD",
  "platformAccountId": "user_123_primary_bank",
  "accountInfo": {
    "accountType": "US_ACCOUNT",
    "accountNumber": "123456789",
    "routingNumber": "021000021",
    "accountCategory": "CHECKING",
    "bankName": "Chase Bank",
    "beneficiary": {
      "beneficiaryType": "INDIVIDUAL",
      "fullName": "John Doe",
      "birthDate": "1990-01-15",
      "nationality": "US",
      "address": {
        "line1": "123 Main Street",
        "city": "San Francisco",
        "state": "CA",
        "postalCode": "94105",
        "country": "US"
      }
    }
  }
}

Business beneficiaries

For business accounts, include business information:
{
  "currency": "USD",
  "platformAccountId": "acme_corp_account",
  "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001",
  "accountInfo": {
    "accountType": "US_ACCOUNT",
    "accountNumber": "987654321",
    "routingNumber": "021000021",
    "accountCategory": "CHECKING",
    "bankName": "Chase Bank",
    "beneficiary": {
      "beneficiaryType": "BUSINESS",
      "businessInfo": {
        "legalName": "Acme Corporation, Inc.",
        "taxId": "EIN-987654321"
      },
      "address": {
        "line1": "456 Business Ave",
        "city": "New York",
        "state": "NY",
        "postalCode": "10001",
        "country": "US"
      }
    }
  }
}

Account status

Beneficiary data may be reviewed for risk and compliance. Only ACTIVE accounts can receive payments. Updates to account data may trigger account re-review.
StatusDescription
PENDINGCreated, awaiting verification
ACTIVEVerified and ready for transactions
UNDER_REVIEWAdditional review required
INACTIVEDisabled, cannot be used

Listing external accounts

List customer accounts

curl -X GET 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001' \
  -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET'

List platform accounts

For platform-wide operations, list all platform-level external accounts:
curl -X GET 'https://api.lightspark.com/grid/2025-10-13/platform/external-accounts' \
  -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET'
Platform external accounts are used for platform-wide operations like depositing funds from external sources.

Best practices

Validate account details before submission:
// US accounts: 9-digit routing, 4-17 digit account number
if (!/^\d{9}$/.test(routingNumber)) {
  throw new Error("Invalid routing number");
}

// CLABE: exactly 18 digits
if (!/^\d{18}$/.test(clabeNumber)) {
  throw new Error("Invalid CLABE number");
}
Verify status before sending payments:
if (account.status !== "ACTIVE") {
  throw new Error(`Account is ${account.status}, cannot process payment`);
}
Never expose full account numbers. Display only masked info:
function displaySafely(account) {
  return {
    id: account.id,
    bankName: account.accountInfo.bankName,
    lastFour: account.accountInfo.accountNumber.slice(-4),
    status: account.status,
  };
}

Using external accounts for ramps

External accounts serve as destinations for ramp conversions:

For on-ramps (Fiat → Crypto)

External accounts represent crypto wallet destinations:
  • Spark wallets: Lightning Network wallets for instant Bitcoin delivery
  • Self-custody: User-controlled wallets for full ownership
  • No beneficiary required: Crypto wallets don’t need compliance information
Spark wallets are the recommended destination for on-ramps due to instant settlement and minimal fees.

For off-ramps (Crypto → Fiat)

External accounts represent bank account destinations:
  • Traditional bank accounts: ACH, wire, SEPA, CLABE, PIX, UPI, etc.
  • Beneficiary required: Full compliance information needed for fiat destinations
  • Multiple currencies: Support for USD, EUR, MXN, BRL, INR, and more
Off-ramp destinations require complete beneficiary information for compliance. Ensure all required fields are provided.

Crypto wallet destinations

Spark wallet addresses

The primary destination type for on-ramps:
curl -X POST 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts' \
  -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' \
  -H 'Content-Type: application/json' \
  -d '{
    "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001",
    "currency": "BTC",
    "platformAccountId": "user_wallet_001",
    "accountInfo": {
      "accountType": "SPARK_WALLET",
      "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu"
    }
  }'
Response:
{
  "id": "ExternalAccount:wallet001",
  "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001",
  "status": "ACTIVE",
  "currency": "BTC",
  "platformAccountId": "user_wallet_001",
  "accountInfo": {
    "accountType": "SPARK_WALLET",
    "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu"
  },
  "createdAt": "2025-10-03T14:00:00Z"
}
Spark wallet external accounts are immediately ACTIVE and ready for on-ramp conversions.

Validate Spark addresses

Before creating external accounts, validate Spark wallet addresses:
function isValidSparkAddress(address) {
  // Spark addresses start with 'spark1' and are 87 characters
  return address.startsWith("spark1") && address.length === 87;
}

const walletAddress =
  "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu";

if (!isValidSparkAddress(walletAddress)) {
  throw new Error("Invalid Spark wallet address format");
}

// Create external account
await createExternalAccount({
  customerId,
  currency: "BTC",
  accountInfo: {
    accountType: "SPARK_WALLET",
    address: walletAddress,
  },
});
Spark addresses are case-insensitive and follow the bech32 format starting with spark1.

Bank account destinations

For off-ramp flows, create external bank accounts with full beneficiary information:

Example: US bank account for off-ramp

curl -X POST 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts' \
  -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' \
  -H 'Content-Type: application/json' \
  -d '{
    "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001",
    "currency": "USD",
    "platformAccountId": "user_bank_usd_001",
    "accountInfo": {
      "accountType": "US_ACCOUNT",
      "accountNumber": "123456789",
      "routingNumber": "021000021",
      "accountCategory": "CHECKING",
      "bankName": "Chase Bank",
      "beneficiary": {
        "beneficiaryType": "INDIVIDUAL",
        "fullName": "John Doe",
        "birthDate": "1990-01-15",
        "nationality": "US",
        "address": {
          "line1": "123 Main Street",
          "city": "San Francisco",
          "state": "CA",
          "postalCode": "94105",
          "country": "US"
        }
      }
    }
  }'

Creating accounts inline with quotes

For one-time conversions, create external accounts inline using externalAccountDetails:
curl -X POST 'https://api.lightspark.com/grid/2025-10-13/quotes' \
  -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET' \
  -H 'Content-Type: application/json' \
  -d '{
    "source": {
      "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001",
      "currency": "USD"
    },
    "destination": {
      "externalAccountDetails": {
        "customerId": "Customer:019542f5-b3e7-1d02-0000-000000000001",
        "currency": "BTC",
        "accountInfo": {
          "accountType": "SPARK_WALLET",
          "address": "spark1pgssyuuuhnrrdjswal5c3s3rafw9w3y5dd4cjy3duxlf7hjzkp0rqx6dj6mrhu"
        }
      }
    },
    "lockedCurrencySide": "SENDING",
    "lockedCurrencyAmount": 10000
  }'
Use externalAccountDetails for one-time destinations. The external account will be automatically created and can be reused for future quotes using its returned ID.

Listing external accounts

List customer external accounts

curl -X GET 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001' \
  -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET'

Filter by currency

curl -X GET 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001&currency=BTC' \
  -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET'

Filter by account type

curl -X GET 'https://api.lightspark.com/grid/2025-10-13/customers/external-accounts?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001&accountType=SPARK_WALLET' \
  -H 'Authorization: Basic $GRID_CLIENT_ID:$GRID_CLIENT_SECRET'

Account status and verification

External accounts move through verification states:
StatusDescriptionCan Use for Conversions
PENDINGVerification in progress
ACTIVEVerified and ready
FAILEDVerification failed
DISABLEDManually disabled
Spark wallet accounts are immediately ACTIVE. Bank accounts may require verification (typically instant to a few hours).

Best practices for ramps

Always validate wallet addresses and bank account details before creating external accounts:
// Validate Spark address format
function validateSparkAddress(address) {
  if (!address.startsWith("spark1")) {
    throw new Error("Spark address must start with spark1");
  }
  if (address.length !== 87) {
    throw new Error("Spark address must be 87 characters");
  }
  // Additional validation logic
  return true;
}
Map external accounts to your internal system using platformAccountId:
const externalAccount = await createExternalAccount({
  customerId,
  currency: "BTC",
  platformAccountId: `${userId}_spark_primary`, // Your internal ID
  accountInfo: {
    accountType: "SPARK_WALLET",
    address: sparkAddress,
  },
});

// Store mapping in your database
await db.userWallets.create({
  userId,
  gridAccountId: externalAccount.id,
  internalId: `${userId}_spark_primary`,
});
Support multiple wallets or bank accounts for flexibility:
// Primary Spark wallet for on-ramps
await createExternalAccount({
  customerId,
  currency: "BTC",
  platformAccountId: `${userId}_spark_primary`,
  accountInfo: { accountType: "SPARK_WALLET", address: primaryWallet },
});

// Secondary wallet for larger amounts
await createExternalAccount({
  customerId,
  currency: "BTC",
  platformAccountId: `${userId}_spark_savings`,
  accountInfo: { accountType: "SPARK_WALLET", address: savingsWallet },
});
For crypto destinations, implement additional verification:
// Verify Spark wallet is reachable (optional)
async function verifySparkWallet(address) {
  try {
    // Use Lightning Network tools to verify wallet exists
    const probe = await lightningClient.probeWallet(address);
    return probe.reachable;
  } catch (error) {
    console.error("Wallet verification failed:", error);
    return false;
  }
}

// Only create external account after verification
if (await verifySparkWallet(sparkAddress)) {
  await createExternalAccount({
    /* ... */
  });
}

Ramp-specific considerations

On-ramp destinations

  • Instant delivery: Spark wallets receive Bitcoin within seconds
  • No KYC required: Self-custody wallets don’t need beneficiary info
  • Reusable addresses: Store and reuse Spark addresses for multiple conversions
  • No minimum: Send any amount supported by Lightning Network

Off-ramp destinations

  • Full compliance: Bank accounts require complete beneficiary information
  • Verification delays: Bank account verification may take a few hours
  • Settlement times: Vary by destination (instant for RTP/PIX, 1-3 days for ACH)
  • Amount limits: Check minimum and maximum amounts per destination currency
Always verify account details before initiating large off-ramp conversions. Test with small amounts first.

Next steps

I