AgentSkillsCN

Bridge External Accounts

利用Bridge.xyz API管理外部银行账户。可通过Plaid或直接调用API绑定银行账户,支持多种账户类型(美国ACH、IBAN、SWIFT、CLABE、PIX)。适用场景包括:创建外部账户、对接Plaid系统、配置出金目的地,以及完成账户验证。

SKILL.md
--- frontmatter
name: Bridge External Accounts
description: External bank account management with Bridge.xyz API. Link bank accounts via Plaid or direct API, support multiple account types (US ACH, IBAN, SWIFT, CLABE, PIX). Use for: creating external accounts, Plaid integration, offramp destinations, and account verification.

Bridge External Accounts

Quick Reference

typescript
type AccountType = 'us' | 'iban' | 'swift' | 'clabe' | 'pix';
type OwnerType = 'individual' | 'business';

Create External Account (Direct API)

US Bank Account

typescript
interface CreateExternalAccountRequest {
  currency: string;
  bank_name: string;
  account_owner_name: string;
  account_number: string;
  routing_number: string;  // Required for US ACH
  account_type: 'us';
  address: {
    street_line1: string;
    street_line2?: string;
    city: string;
    state: string;
    postal_code: string;
    country: string;
  };
  account_owner_type?: OwnerType;
  first_name?: string;      // Required if individual
  last_name?: string;       // Required if individual
  business_name?: string;   // Required if business
}

interface ExternalAccount {
  id: string;
  customer_id: string;
  type: AccountType;
  currency: string;
  bank_name: string;
  account_owner_name: string;
  account_number_masked: string;
  routing_number_masked?: string;
  account_type: string;
  status: 'active' | 'pending' | 'failed';
  created_at: string;
}

async function createUSBankAccount(
  customerId: string,
  account: Omit<CreateExternalAccountRequest, 'currency' | 'account_type'>
): Promise<ExternalAccount> {
  const response = await fetch(`${BRIDGE_API_URL}/external_accounts`, {
    method: 'POST',
    headers: {
      'Api-Key': API_KEY,
      'Idempotency-Key': crypto.randomUUID(),
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      currency: 'USD',
      account_type: 'us',
      ...account,
    }),
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`External account creation failed: ${error.message}`);
  }

  return response.json();
}

// Usage
const usAccount = await createUSBankAccount('cust_alice', {
  bank_name: 'Chase Bank',
  account_owner_name: 'John Doe',
  account_number: '1234567890',
  routing_number: '021000021',
  address: {
    street_line1: '123 Main St',
    city: 'New York',
    state: 'NY',
    postal_code: '10001',
    country: 'US',
  },
  account_owner_type: 'individual',
  first_name: 'John',
  last_name: 'Doe',
});

IBAN (SEPA/European)

typescript
interface IBANDetails {
  country_code: string;  // e.g., 'DE', 'FR', 'IE'
  check_digits: string;
  bank_code: string;
  account_number: string;
}

async function createIBANAccount(
  customerId: string,
  details: {
    bank_name: string;
    account_owner_name: string;
    iban: IBANDetails;
    address: CreateExternalAccountRequest['address'];
    account_owner_type: OwnerType;
    first_name?: string;
    last_name?: string;
    business_name?: string;
  }
): Promise<ExternalAccount> {
  const response = await fetch(`${BRIDGE_API_URL}/external_accounts`, {
    method: 'POST',
    headers: {
      'Api-Key': API_KEY,
      'Idempotency-Key': crypto.randomUUID(),
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      currency: 'EUR',
      account_type: 'iban',
      bank_name: details.bank_name,
      account_owner_name: details.account_owner_name,
      iban: details.iban,
      address: details.address,
      account_owner_type: details.account_owner_type,
      first_name: details.first_name,
      last_name: details.last_name,
      business_name: details.business_name,
    }),
  });

  if (!response.ok) {
    throw new Error('IBAN account creation failed');
  }

  return response.json();
}

// Usage
const ibanAccount = await createIBANAccount('cust_european', {
  bank_name: 'Deutsche Bank',
  account_owner_name: 'Hans Mueller',
  address: {
    street_line1: 'Unter den Linden 77',
    city: 'Berlin',
    state: '',
    postal_code: '10117',
    country: 'DE',
  },
  account_owner_type: 'individual',
  first_name: 'Hans',
  last_name: 'Mueller',
  iban: {
    country_code: 'DE',
    check_digits: '89',
    bank_code: '50070000',
    account_number: '0245478900',
  },
});

SWIFT International

typescript
async function createSWIFTAccount(
  customerId: string,
  details: {
    bank_name: string;
    account_owner_name: string;
    swift: { bic: string; account_number: string };
    address: CreateExternalAccountRequest['address'];
    account_owner_type: OwnerType;
  }
): Promise<ExternalAccount> {
  const response = await fetch(`${BRIDGE_API_URL}/external_accounts`, {
    method: 'POST',
    headers: {
      'Api-Key': API_KEY,
      'Idempotency-Key': crypto.randomUUID(),
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      currency: 'USD',
      account_type: 'swift',
      bank_name: details.bank_name,
      account_owner_name: details.account_owner_name,
      swift: details.swift,
      address: details.address,
      account_owner_type: details.account_owner_type,
    }),
  });

  if (!response.ok) {
    throw new Error('SWIFT account creation failed');
  }

  return response.json();
}

Mexican CLABE (SPEI)

typescript
async function createCLABEAccount(
  customerId: string,
  details: {
    bank_name: string;
    account_owner_name: string;
    clabe: { clabe: string };
    address: CreateExternalAccountRequest['address'];
    account_owner_type: OwnerType;
  }
): Promise<ExternalAccount> {
  const response = await fetch(`${BRIDGE_API_URL}/external_accounts`, {
    method: 'POST',
    headers: {
      'Api-Key': API_KEY,
      'Idempotency-Key': crypto.randomUUID(),
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      currency: 'MXN',
      account_type: 'clabe',
      bank_name: details.bank_name,
      account_owner_name: details.account_owner_name,
      clabe: details.clabe,
      address: details.address,
      account_owner_type: details.account_owner_type,
    }),
  });

  if (!response.ok) {
    throw new Error('CLABE account creation failed');
  }

  return response.json();
}

Brazilian PIX

typescript
async function createPIXAccount(
  customerId: string,
  details: {
    bank_name: string;
    account_owner_name: string;
    pix: {
      tax_id: string;          // CPF or CNPJ
      account_number: string;
      branch_id: string;
      account_type: 'checking' | 'savings';
    };
    address: CreateExternalAccountRequest['address'];
    account_owner_type: OwnerType;
  }
): Promise<ExternalAccount> {
  const response = await fetch(`${BRIDGE_API_URL}/external_accounts`, {
    method: 'POST',
    headers: {
      'Api-Key': API_KEY,
      'Idempotency-Key': crypto.randomUUID(),
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      currency: 'BRL',
      account_type: 'pix',
      bank_name: details.bank_name,
      account_owner_name: details.account_owner_name,
      pix: details.pix,
      address: details.address,
      account_owner_type: details.account_owner_type,
    }),
  });

  if (!response.ok) {
    throw new Error('PIX account creation failed');
  }

  return response.json();
}

Plaid Integration

typescript
// Step 1: Create Plaid Link Token
interface PlaidLinkTokenResponse {
  link_token: string;
  link_token_expires_at: string;
  callback_url: string;
}

async function createPlaidLinkToken(customerId: string): Promise<PlaidLinkTokenResponse> {
  const response = await fetch(
    `${BRIDGE_API_URL}/customers/${customerId}/plaid_link_requests`,
    {
      method: 'POST',
      headers: {
        'Api-Key': API_KEY,
        'Idempotency-Key': crypto.randomUUID(),
        'Content-Type': 'application/json',
      },
    }
  );

  if (!response.ok) {
    throw new Error('Failed to create Plaid link token');
  }

  return response.json();
}

// Usage - Frontend integration
async function initiatePlaidLink(customerId: string) {
  const { link_token, callback_url } = await createPlaidLinkToken(customerId);
  
  // Send link_token to frontend
  // Frontend uses Plaid Link SDK with link_token
  // After user completes Plaid, they redirect to callback_url
  
  return { link_token, callback_url };
}

Handle Plaid Callback

typescript
// Exchange public_token for access (handled by Bridge)
// Your webhook receives notification when account is linked

async function listLinkedAccounts(customerId: string): Promise<ExternalAccount[]> {
  const response = await fetch(
    `${BRIDGE_API_URL}/customers/${customerId}/external_accounts`,
    {
      headers: { 'Api-Key': API_KEY },
    }
  );

  if (!response.ok) {
    throw new Error('Failed to list external accounts');
  }

  return response.json();
}

// Usage
const accounts = await listLinkedAccounts('cust_alice');
accounts.forEach(acc => {
  console.log(`Account: ${acc.bank_name} - ****${acc.account_number_masked}`);
  console.log(`Status: ${acc.status}`);
});

Account Management

typescript
class ExternalAccountManager {
  async getAccount(accountId: string): Promise<ExternalAccount> {
    const response = await fetch(
      `${BRIDGE_API_URL}/external_accounts/${accountId}`,
      { headers: { 'Api-Key': API_KEY } }
    );

    if (!response.ok) {
      throw new Error('Account not found');
    }

    return response.json();
  }

  async deleteAccount(accountId: string): Promise<void> {
    const response = await fetch(
      `${BRIDGE_API_URL}/external_accounts/${accountId}`,
      {
        method: 'DELETE',
        headers: { 'Api-Key': API_KEY },
      }
    );

    if (!response.ok) {
      throw new Error('Failed to delete account');
    }
  }

  async listCustomerAccounts(customerId: string): Promise<ExternalAccount[]> {
    return listLinkedAccounts(customerId);
  }

  async getDefaultAccount(customerId: string): Promise<ExternalAccount | null> {
    const accounts = await this.listCustomerAccounts(customerId);
    return accounts.find(a => a.status === 'active') || null;
  }
}

Offramp to External Account

typescript
async function offrampToBank(
  customerId: string,
  externalAccountId: string,
  amount: string,
  cryptoAddress: string
): Promise<Transfer> {
  return createTransfer({
    amount,
    on_behalf_of: customerId,
    source: {
      payment_rail: 'ethereum',
      currency: 'usdc',
      from_address: cryptoAddress,
    },
    destination: {
      payment_rail: 'ach',
      currency: 'usd',
      external_account_id: externalAccountId,
    },
  });
}

// Offramp service
class OfframpService {
  async cashOutUS(
    customerId: string,
    amount: string,
    walletAddress: string
  ): Promise<Transfer> {
    const accounts = await new ExternalAccountManager().listCustomerAccounts(customerId);
    const usAccount = accounts.find(a => a.type === 'us');
    
    if (!usAccount) {
      throw new Error('No US bank account linked');
    }

    return offrampToBank(customerId, usAccount.id, amount, walletAddress);
  }

  async cashOutEU(
    customerId: string,
    amount: string,
    walletAddress: string
  ): Promise<Transfer> {
    const accounts = await new ExternalAccountManager().listCustomerAccounts(customerId);
    const ibanAccount = accounts.find(a => a.type === 'iban');
    
    if (!ibanAccount) {
      throw new Error('No IBAN account linked');
    }

    return createTransfer({
      amount,
      on_behalf_of: customerId,
      source: {
        payment_rail: 'ethereum',
        currency: 'usdc',
        from_address: walletAddress,
      },
      destination: {
        payment_rail: 'sepa',
        currency: 'eur',
        external_account_id: ibanAccount.id,
      },
    });
  }
}

Multi-Region Setup

typescript
class GlobalBankingSetup {
  async setupUSAccount(customerId: string): Promise<ExternalAccount> {
    return createUSBankAccount(customerId, {
      bank_name: '',
      account_owner_name: '',
      account_number: '',
      routing_number: '',
      address: { street_line1: '', city: '', state: '', postal_code: '', country: 'US' },
      account_owner_type: 'individual',
      first_name: '',
      last_name: '',
    });
  }

  async setupEUAccount(customerId: string): Promise<ExternalAccount> {
    return createIBANAccount(customerId, {
      bank_name: '',
      account_owner_name: '',
      address: { street_line1: '', city: '', state: '', postal_code: '', country: '' },
      account_owner_type: 'individual',
      iban: { country_code: '', check_digits: '', bank_code: '', account_number: '' },
    });
  }

  async setupMXAccount(customerId: string): Promise<ExternalAccount> {
    return createCLABEAccount(customerId, {
      bank_name: '',
      account_owner_name: '',
      address: { street_line1: '', city: '', state: '', postal_code: '', country: 'MX' },
      account_owner_type: 'individual',
      clabe: { clabe: '' },
    });
  }

  async setupBRAccount(customerId: string): Promise<ExternalAccount> {
    return createPIXAccount(customerId, {
      bank_name: '',
      account_owner_name: '',
      address: { street_line1: '', city: '', state: '', postal_code: '', country: 'BR' },
      account_owner_type: 'individual',
      pix: { tax_id: '', account_number: '', branch_id: '', account_type: 'checking' },
    });
  }

  async setupAllRegions(customerId: string): Promise<ExternalAccount[]> {
    return Promise.all([
      this.setupUSAccount(customerId),
      this.setupEUAccount(customerId),
      this.setupMXAccount(customerId),
      this.setupBRAccount(customerId),
    ]);
  }
}

Webhook Events

  • external_account.created - External account linked
  • external_account.verified - Account verification completed
  • external_account.deleted - Account removed
  • transfer.to_external_account.completed - Offramp completed

Best Practices

  1. Plaid for US - Preferred for US bank linking
  2. Direct API for others - IBAN, SWIFT, CLABE, PIX
  3. Verify accounts - Check status before offramping
  4. Store account IDs - Required for transfers
  5. Multiple accounts - Support different regions
  6. Idempotency - Use Idempotency-Key for all operations