Skip to main content
Bridge Cards provides the native capability to issue cards that spend directly from a non-custodial wallet, without requiring a separate Bridge Wallet or card balance wallet to be set up. This has the advantage of enabling your customers to spend directly off of their main stablecoin balance without needing to constantly top up a separate wallet for the card balance. You can enable this by setting up an approval or allowance from your wallet for Bridge’s smart contract to spend from it. This doc provides an overview of Bridge’s direct pull capabilities, and how you can set them up for your wallet.

Solana

Bridge has a program deployed on cardWArqhdV5jeRXXjUti7cHAa4mj41Nj3Apc6RPZH2 on both Solana mainnet and Solana devnet. To enable direct spend against Solana accounts, your account must sign a transaction approving Bridge’s cards program delegate address to spend a specific stablecoin token from your account. You can also see the full source of the program in the bridge-cards GitHub repo. In this example, we’ll enable Bridge’s Solana program to spend USDC from a non-custodial wallet.

Step 1: Provision a card account

In the request to the endpoint to provision a card account, specify the wallet and currency to use. For the crypto_account type, specify standard to indicate that Bridge should pull from the non-custodial wallet. Note that a wallet can only be tied to one card account. Bridge does not allow issuing multiple cards that spend from the same wallet.
Do not specify the associated token account address in this request. Bridge will automatically derive it from the address and currency specified. In this example, the account’s owner address is BDkZQv1DqS7RJG5MZjVEP8FbN9Xvpf5b67kpi3765rQb but the USDC token account is GXaRe925ejuzX3KZdtLPJ8fpnudzhdk9eXDxNSEdro49.
{
    "chain": "solana",
    "currency": "usdc",
    "crypto_account": {
        "type": "standard",
        "address": "BDkZQv1DqS7RJG5MZjVEP8FbN9Xvpf5b67kpi3765rQb"
    }
}
When the card account is created, Bridge will submit an onchain transaction to register the program delegate address associated with the specified crypto_account address. Here is a sample transaction doing so. For safety reasons, we recommend provisioning the card account before setting up the delegate approval, so that Bridge ties this address to the customer before any approvals are submitted onchain.

Step 2: Set up the delegate approval

For this example, we’ll be using Solana’s Javascript SDK to set up an approval to the Bridge card program’s associated Program-Derived Address. We’ve provided a consolidated version of this delegate approval logic in a GitHub Gist, but this section will explain each step in detail.

Step 2a: Setup connection and transaction

First, we set up a new transaction to contain our instructions. We also ensure that the account has a non-zero amount of SOL so that we can submit transactions onchain.
const connection = new Connection(clusterApiUrl("mainnet-beta"), "confirmed");
const transaction = new Transaction();

const userKeypair = loadKeypairFromJson(...);
console.log('Public key:', userKeypair.publicKey.toString());

// Check SOL balance
const balance = await connection.getBalance(userKeypair.publicKey);
console.log('SOL balance:', balance / LAMPORTS_PER_SOL, 'SOL');

if (balance === 0) {
  console.log('⚠️  No SOL balance. You need SOL for transaction fees.');
  return;
}

Step 2b: Setup ATA if necessary

If necessary, first set up the associated token account for your wallet to hold a balance in the token you want to spend. This section checks for the existence of the associated token account, and adds an instruction into the transaction to create the ATA if it doesn’t already exist.
// Get the user's token account for the given currency.
const MINT_PUBKEY = new PublicKey(
  "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"  // https://solscan.io/token/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
);  
const userAta = getAssociatedTokenAddressSync(
  MINT_PUBKEY,
  userKeypair.publicKey,
  false,
  TOKEN_PROGRAM_ID  // note: depending on the currency, might need to be TOKEN_2022_PROGRAM_ID
);
console.log('Associated Token Account:', userAta.toString());

// Check if the ATA exists
let ataExists = false;
try {
  await getAccount(connection, userAta, undefined, TOKEN_PROGRAM_ID);
  ataExists = true;
  console.log('✅ Associated Token Account exists');
} catch (error) {
  console.log('❌ Associated Token Account does not exist, will create it');
	
  const createAtaInstruction = createAssociatedTokenAccountInstruction(
    userKeypair.publicKey, // payer
    userAta, // ata
    userKeypair.publicKey, // owner
    MINT_PUBKEY, // mint
    TOKEN_PROGRAM_ID
  );
  transaction.add(createAtaInstruction);
  console.log('Added instruction to create ATA');
}

Step 2c: Set up program delegate approval

Next, we’ll add an instruction to the transaction to approve Bridge’s card program to spend from the specified ATA. Note that the MERCHANT_ID to use when computing the delegate is specific to your developer. You will be assigned a specific MERCHANT_ID value by Bridge.

const PROGRAM_ID = new PublicKey("cardWArqhdV5jeRXXjUti7cHAa4mj41Nj3Apc6RPZH2");
const MINT_DECIMALS = 6;
const APPROVAL_AMOUNT_UI = 100;
const APPROVAL_AMOUNT = BigInt(APPROVAL_AMOUNT_UI * 10 ** MINT_DECIMALS);
const bridgeSdk = new BridgeSDK(PROGRAM_ID);
const [delegatePda] = bridgeSdk.findUserDelegatePDA(
  MERCHANT_ID,
  MINT_PUBKEY,
  userAta
);

console.log('Delegate PDA:', delegatePda.toString());

// Approve the BridgeCard contract to manage the user's token account.
const approveInstruction = createApproveInstruction(
  userAta,
  delegatePda,
  userKeypair.publicKey,
  APPROVAL_AMOUNT,
  [],
  TOKEN_PROGRAM_ID
);

transaction.add(approveInstruction);
console.log('Added approve instruction');

// You could optionally pay these fees on behalf of your users.
transaction.feePayer = userKeypair.publicKey;

Step 2d: Submit approval transaction

Finally, you will submit the transaction onchain:
try {
  console.log('Sending transaction...');
  const signature = await sendAndConfirmTransaction(connection, transaction, [
    userKeypair,
  ]);

  console.log('✅ Transaction successful!');
  console.log('Signature:', signature);
  console.log(`View on Solana Explorer: https://explorer.solana.com/tx/${signature}`);
} catch (error: any) {
  console.error('❌ Error sending and confirming transaction:', error);
  
  // Enhanced error logging
  if (error.logs) {
    console.log('Transaction logs:', error.logs);
  }
  throw error;
}
The result will be a delegate approval that resembles this transaction.

Step 3: Consume webhook events for card transactions

The card is now ready to use. When the card is used for a purchase at a merchant, you will receive webhook events informing you about it. This webhook event will contain information about both the card network authorization and the onchain transaction. Transactions will be submitted onchain at the time of card authorization, but complete asynchronously. This will cause two webhook events to be published. When the card authorization initially occurs, you will receive a webhook like the following, without the crypto transaction details:
{
  "api_version": "v0",
  "event_id": "wh_tuvB3rbeBKREPEgZ2jmnB4k",
  "event_developer_id": "d76d99b4-f4e8-4799-9907-10c965e0e9fc",
  "event_sequence": 13426,
  "event_category": "card_transaction",
  "event_type": "card_transaction.created",
  "event_object_id": "86d30f38-5ea0-402d-ad48-48003d3b3f29",
  "event_object_status": "approved",
  "event_object": {
    "id": "86d30f38-5ea0-402d-ad48-48003d3b3f29",
    "amount": "-10.0",
    "status": "approved",
    "category": "purchase",
    "currency": "usd",
    "created_at": "2025-12-11T17:47:32.152Z",
    "updated_at": "2025-12-11T17:47:45.153Z",
    "customer_id": "2c21ddbb-cf34-4170-b9b2-076a3ed55de9",
    "authorized_at": "2025-12-11T17:47:32.167Z",
    "merchant_name": "BRIDGE CAFE, SAN FRANCISCO, CA",
    "status_reason": "approved",
    "billing_amount": "-10.0",
    "card_account_id": "3d698985-fbd4-4889-999a-4fa7bd578452",
    "original_amount": "-10.0",
    "merchant_location": "+18886772541, CAUS",
    "authorization_infos": [
      {
        "amount": "-10.0",
        "wallet": "other",
        "account": {
          "last_4": "2960"
        },
        "network": "visa",
        "currency": "usd",
        "merchant": {
          "state": "CA",
          "country": "USA",
          "description": "BRIDGE CAFE, SAN FRANCISCO, CA",
          "postal_code": "94306",
          "category_code": "6012"
        },
        "auth_type": "auth",
        "recurring": false,
        "created_at": "2025-12-11T17:47:31.000Z",
        "fee_amount": "0.00",
        "customer_id": "2c21ddbb-cf34-4170-b9b2-076a3ed55de9",
        "card_present": false,
        "entry_method": "card_not_present",
        "international": false,
        "status_reason": "approved",
        "billing_amount": "-10.0",
        "transaction_id": "86d30f38-5ea0-402d-ad48-48003d3b3f29",
        "approval_status": "approved",
        "card_account_id": "3d698985-fbd4-4889-999a-4fa7bd578452",
        "cashback_amount": "0.00",
        "authorization_id": "3aa53334-29c5-4db2-b5fc-bf0a330a7c4c",
        "partial_supported": false,
        "verification_data": {
          "cvv_check": "not_provided",
          "pin_check": "no_pin_passed",
          "address_check": "mismatch",
          "three_d_secure_check": "authenticated",
          "address_postal_code_check": "mismatch"
        },
        "local_transaction_details": {
          "amount": "-10.0",
          "currency": "usd",
          "exchange_rate": "1.0"
        }
      }
    ],
    "merchant_category_code": "6012",
    "transaction_description": "BRIDGE CAFE, SAN FRANCISCO, CA"
  },
  "event_object_changes": {},
  "event_created_at": "2025-12-11T17:47:45.900Z"
}
Subsequently (on the order of seconds) when the crypto transaction is confirmed onchain, another webhook event will be published with the details:
{
  "api_version": "v0",
  "event_id": "wh_tdcF5jcwFPT1cg5mvmeKMG1",
  "event_developer_id": "d76d99b4-f4e8-4799-9907-10c965e0e9fc",
  "event_sequence": 13428,
  "event_category": "card_transaction",
  "event_type": "card_transaction.updated",
  "event_object_id": "86d30f38-5ea0-402d-ad48-48003d3b3f29",
  "event_object_status": "approved",
  "event_object": {
    "id": "86d30f38-5ea0-402d-ad48-48003d3b3f29",
    "amount": "-10.0",
    "status": "approved",
    "category": "purchase",
    "currency": "usd",
    "created_at": "2025-12-11T17:47:32.152Z",
    "updated_at": "2025-12-11T17:47:51.785Z",
    "customer_id": "2c21ddbb-cf34-4170-b9b2-076a3ed55de9",
    "authorized_at": "2025-12-11T17:47:32.167Z",
    "merchant_name": "BRIDGE CAFE, SAN FRANCISCO, CA",
    "status_reason": "approved",
    "billing_amount": "-10.0",
    "card_account_id": "3d698985-fbd4-4889-999a-4fa7bd578452",
    "original_amount": "-10.0",
    "merchant_location": "+18886772541, CAUS",
    "authorization_infos": [
      {
        "amount": "-10.0",
        "wallet": "other",
        "account": {
          "last_4": "2960"
        },
        "network": "visa",
        "currency": "usd",
        "merchant": {
          "state": "CA",
          "country": "USA",
          "description": "BRIDGE CAFE, SAN FRANCISCO, CA",
          "postal_code": "94306",
          "category_code": "6012"
        },
        "auth_type": "auth",
        "recurring": false,
        "created_at": "2025-12-11T17:47:31.000Z",
        "fee_amount": "0.00",
        "customer_id": "2c21ddbb-cf34-4170-b9b2-076a3ed55de9",
        "card_present": false,
        "entry_method": "card_not_present",
        "international": false,
        "status_reason": "approved",
        "billing_amount": "-10.0",
        "crypto_details": {
          "chain": "solana",
          "amount": "10.0",
          "tx_hash": "618p4RWZ4UPe6uCeFo0BaRb2H4gSQJjLayDihFD7fERAnfyoxyMJQZc4WmKkPkLu7QHnXgpWp6pPUMqc2HzGqH3",
          "currency": "usdc",
          "tx_reference": "d0b935bf-300e-431b-82bd-311ef7115f86"
        },
        "transaction_id": "86d30f38-5ea0-402d-ad48-48003d3b3f29",
        "approval_status": "approved",
        "card_account_id": "3d698985-fbd4-4889-999a-4fa7bd578452",
        "cashback_amount": "0.00",
        "authorization_id": "c9537b55-094f-4bd5-b8f5-4a27c37ce83d",
        "partial_supported": false,
        "verification_data": {
          "cvv_check": "not_provided",
          "pin_check": "no_pin_passed",
          "address_check": "mismatch",
          "three_d_secure_check": "authenticated",
          "address_postal_code_check": "mismatch"
        },
        "local_transaction_details": {
          "amount": "-10.0",
          "currency": "usd",
          "exchange_rate": "1.0"
        }
      }
    ],
    "merchant_category_code": "6012",
    "transaction_description": "BRIDGE CAFE, SAN FRANCISCO, CA"
  },
  "event_object_changes": {
    ...
  },
  "event_created_at": "2025-12-11T17:47:52.380Z"
}
You can see more card transaction scenarios in the Card Transaction Webhooks section of the documentation. Incremental authorizations will cause additional transactions to be submitted onchain to cover the additional charge.

EVM

EVM support is forthcoming in Q4 2025. Please reach out to Bridge if you would like to enable direct pull on any specific EVM-compatible chain.