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