Skip to main content
Make sure you’ve completed the installation before proceeding.

Initialize the Client

Create an Align client instance with your API credentials:
import Align from "@tolbel/align";

const align = new Align({
  apiKey: process.env.ALIGN_API_KEY!,
  environment: "sandbox", // Use "production" for live transactions
});

Configuration Options

OptionTypeDefaultDescription
apiKeystring-Your AlignLab API key (required)
environment"sandbox" | "production""production"API environment
timeoutnumber30000Request timeout in milliseconds
maxRetriesnumber3Max retry attempts for failed requests
enableLoggingbooleanfalseEnable request/response logging

Core Workflow

Here’s the typical integration flow for processing payments:
1

Create a Customer

Register a new user in the Align system:
const customer = await align.customers.create({
  email: "[email protected]",
  type: "individual",
  first_name: "Alice",
  last_name: "Smith",
});

console.log(`Customer ID: ${customer.customer_id}`);
// Customer ID: 123e4567-e89b-12d3-a456-426614174000
For businesses, use type: "corporate" and provide company_name instead of first/last name.
2

Complete KYC Verification

Generate a KYC verification link for your user:
const kycSession = await align.customers.createKycSession(
  customer.customer_id
);

// Redirect user to complete verification
console.log(`KYC Link: ${kycSession.kycs.kyc_flow_link}`);
// https://kyc.alignlabs.dev/flow/...
Users must complete KYC before creating virtual accounts or processing transfers.
In Sandbox, you can simulate KYC approval:
await align.customers.simulateCustomer({
  customer_id: customer.customer_id,
  action: "kyc.status.approve",
});
3

Create a Virtual Account

Once KYC is approved, create a virtual bank account for deposits:
const virtualAccount = await align.virtualAccounts.create(
  customer.customer_id,
  {
    source_currency: "usd",
    // source_rails: "ach", // Removed: Only 'swift' is supported, other rails are automatic
    destination_token: "usdc",
    destination_network: "polygon",
    destination_address: "0x742d35Cc6634C0532925a3b844Bc9e7595f0aB42",
  }
);

// Get deposit instructions (Type-safe check for US accounts)
const instructions = virtualAccount.deposit_instructions;
if ("us" in instructions) {
  console.log(`Account Number: ${instructions.us.account_number}`);
  console.log(`Routing Number: ${instructions.us.routing_number}`);
}
Deposit instructions vary by payment rails. ACH accounts have routing numbers, while IBAN accounts have BIC/SWIFT codes.
4

Process Deposits

When users deposit fiat to their virtual account, funds are automatically converted and sent to their crypto wallet.Simulate a deposit in sandbox:
await align.virtualAccounts.simulate(virtualAccount.id, {
  amount: "100.00",
});

// Check virtual account for updated balance
const updated = await align.virtualAccounts.get(virtualAccount.id);
console.log(`Status: ${updated.status}`);

Creating Transfers

For more control over conversions, use the Transfers API:

Onramp (Fiat → Crypto)

// Step 1: Get a quote
const quote = await align.transfers.createOnrampQuote(customer.customer_id, {
  source_amount: "100.00",
  source_currency: "usd",
  source_payment_rails: "ach",
  destination_token: "usdc",
  destination_network: "polygon",
});

console.log(`You'll receive: ${quote.destination_amount} USDC`);
console.log(`Fee: ${quote.fee_amount} USD`);

// Step 2: Execute the transfer
const transfer = await align.transfers.createOnrampTransfer(
  customer.customer_id,
  {
    quote_id: quote.quote_id,
    destination_wallet_address: "0x...",
  }
);

Offramp (Crypto → Fiat)

// Step 1: Link a bank account
const externalAccount = await align.externalAccounts.create(
  customer.customer_id,
  {
    type: "us",
    account_holder_name: "Alice Smith",
    routing_number: "021000021",
    account_number: "123456789",
    account_type: "checking",
  }
);

// Step 2: Get a quote
const quote = await align.transfers.createOfframpQuote(customer.customer_id, {
  source_amount: "100.00",
  source_token: "usdc",
  source_network: "polygon",
  destination_currency: "usd",
  destination_payment_rails: "ach",
});

// Step 3: Execute the transfer
const transfer = await align.transfers.createOfframpTransfer(
  customer.customer_id,
  {
    quote_id: quote.quote_id,
    external_account_id: externalAccount.external_account_id,
    source_wallet_address: "0x...",
  }
);

Error Handling

Always wrap API calls in try-catch blocks:
import Align, { AlignError, AlignValidationError } from "@tolbel/align";

try {
  const customer = await align.customers.create({
    email: "invalid-email",
    type: "individual",
    first_name: "Alice",
    last_name: "Smith",
  });
} catch (error) {
  if (error instanceof AlignValidationError) {
    // Request validation failed (before API call)
    console.error("Validation Error:", error.errors);
  } else if (error instanceof AlignError) {
    // API returned an error
    console.error("API Error:", error.message);
    console.error("Status:", error.statusCode);
  } else {
    // Network or other error
    throw error;
  }
}

Error Handling Guide

Learn more about error types and handling strategies

Webhook Integration

Set up webhooks to receive real-time notifications:
// Register a webhook endpoint
const webhook = await align.webhooks.create({
  url: "https://your-app.com/webhooks/align",
});

console.log(`Webhook ID: ${webhook.id}`);
Webhook signatures are signed using your API Key. See the Webhook Verification guide.

Next Steps

Customer API

Learn about customer management

Virtual Accounts

Deep dive into virtual accounts

Transfers API

Explore transfer options

Error Handling

Handle errors gracefully