Skip to main content

Overview

Card linking allows users to connect their custodial internal wallets to their payment cards. Once linked, card transactions automatically deduct funds from the connected wallets, enabling seamless cryptocurrency spending for everyday purchases.

How It Works

When a card transaction occurs:
  1. Platform checks the highest priority linked wallet
  2. If balance is sufficient, funds are deducted
  3. If insufficient, platform moves to next priority wallet
  4. Process continues until transaction succeeds or all wallets exhausted

Linking Wallets

Link an internal wallet to a user’s card:
curl -X POST "https://api.example.com/v1/wallet/internal/card_linked" \
  -H "x-client-key: YOUR_PUBLIC_KEY" \
  -H "Authorization: Bearer USER_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "addressId": "7c1839ee-918e-4787-b74f-deeb48ead58b"
  }'

Getting the addressId

The addressId is returned when you retrieve internal wallets:
curl -X GET "https://api.example.com/v1/wallet/internal" \
  -H "x-client-key: YOUR_PUBLIC_KEY" \
  -H "Authorization: Bearer USER_ACCESS_TOKEN"
[
  {
    "id": "098aeb90-e7f7-4f81-bc2e-4963330122c5",
    "balance": "150.50",
    "currency": "usdc",
    "address": "0x0a4b21fa733e9aeaddbf070302a85c559de13c4d",
    "addressId": "7c1839ee-918e-4787-b74f-deeb48ead58b",  // Use this value
    "type": "INTERNAL"
  }
]
Each user can link up to 5 wallets to their card. Attempting to link more than 5 wallets returns a validation error.

Viewing Linked Wallets

Get all wallets currently linked to the user’s card:
curl -X GET "https://api.example.com/v1/wallet/internal/card_linked" \
  -H "x-client-key: YOUR_PUBLIC_KEY" \
  -H "Authorization: Bearer USER_ACCESS_TOKEN"

Response Fields

FieldDescription
idLinked wallet identifier
addressBlockchain address of the wallet
currencyCurrency held in the wallet
networkBlockchain network
priorityPayment priority (lower numbers charged first)

Unlinking Wallets

Remove a wallet from card linking:
curl -X DELETE "https://api.example.com/v1/wallet/internal/card_linked" \
  -H "x-client-key: YOUR_PUBLIC_KEY" \
  -H "Authorization: Bearer USER_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "addressId": "7c1839ee-918e-4787-b74f-deeb48ead58b"
  }'
Unlinking a wallet does not affect the wallet itself or its balance. The wallet remains accessible for deposits and withdrawals.

Priority Management

Priority determines which wallet is charged first for card transactions. Lower priority numbers have higher precedence (priority 1 is charged before priority 2).

Viewing Current Priority

curl -X GET "https://api.example.com/v1/wallet/internal/card_linked" \
  -H "x-client-key: YOUR_PUBLIC_KEY" \
  -H "Authorization: Bearer USER_ACCESS_TOKEN"

Updating Priority

Change the priority order of linked wallets:
curl -X PUT "https://api.example.com/v1/wallet/internal/card_linked/priority" \
  -H "x-client-key: YOUR_PUBLIC_KEY" \
  -H "Authorization: Bearer USER_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "wallets": [
      {
        "addressId": "7c1839ee-918e-4787-b74f-deeb48ead58b",
        "priority": 1
      },
      {
        "addressId": "8d9e0f1g-2h3i-4j5k-6l7m-8n9o0p1q2r3s",
        "priority": 2
      },
      {
        "addressId": "9e0f1g2h-3i4j-5k6l-7m8n-9o0p1q2r3s4t",
        "priority": 3
      }
    ]
  }'
You must provide ALL linked wallets when updating priority. The API replaces the entire priority configuration with the provided list.

Priority Rules

  1. Ascending Order: Priority 1 is checked first, then 2, then 3, etc.
  2. Sequential Processing: Platform checks each wallet in order until transaction succeeds
  3. Balance-Based: If a wallet has insufficient balance, platform moves to next priority
  4. No Gaps: Priority numbers should be sequential (1, 2, 3) without gaps

Common Use Cases

Link wallets with different currencies (USDC, USDT, SOL) and set priority based on which currency you prefer to spend first.Example:
  • Priority 1: USDC on Linea (lowest fees)
  • Priority 2: USDT on Ethereum (backup)
  • Priority 3: SOL on Solana (last resort)
Prioritize wallets on networks with lower transaction fees. For example, set Linea wallets to priority 1 (lower fees) and Ethereum wallets to priority 2 (higher fees).Example:
  • Priority 1: USDC on Linea ($0.01 avg fee)
  • Priority 2: USDC on Ethereum ($2-5 avg fee)
Set priority based on wallet balances. Use wallets with smaller balances first to consolidate funds, or use larger balance wallets first to keep smaller amounts as reserves.Example (Spend Small First):
  • Priority 1: Wallet with $50 balance
  • Priority 2: Wallet with $500 balance
If certain wallets offer cashback or rewards, prioritize those wallets to maximize benefits.Example:
  • Priority 1: Wallet with 2% cashback
  • Priority 2: Wallet with 1% cashback
  • Priority 3: Standard wallet

Implementation Examples

Complete Linking Flow

async function linkWalletToCard(userToken, currency) {
  // 1. Get available internal wallets
  const walletsResponse = await fetch(
    'https://api.example.com/v1/wallet/internal',
    {
      headers: {
        'x-client-key': 'YOUR_PUBLIC_KEY',
        'Authorization': `Bearer ${userToken}`
      }
    }
  );

  const wallets = await walletsResponse.json();

  // 2. Find wallet with specified currency
  const wallet = wallets.find(w => w.currency === currency);

  if (!wallet) {
    throw new Error(`No ${currency} wallet found`);
  }

  // 3. Link wallet to card
  const linkResponse = await fetch(
    'https://api.example.com/v1/wallet/internal/card_linked',
    {
      method: 'POST',
      headers: {
        'x-client-key': 'YOUR_PUBLIC_KEY',
        'Authorization': `Bearer ${userToken}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        addressId: wallet.addressId
      })
    }
  );

  return await linkResponse.json();
}

// Usage
await linkWalletToCard(userToken, 'usdc');

Priority Management UI

import { useState, useEffect } from 'react';

function WalletPriorityManager({ userToken }) {
  const [linkedWallets, setLinkedWallets] = useState([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    fetchLinkedWallets();
  }, []);

  const fetchLinkedWallets = async () => {
    const response = await fetch(
      'https://api.example.com/v1/wallet/internal/card_linked',
      {
        headers: {
          'x-client-key': 'YOUR_PUBLIC_KEY',
          'Authorization': `Bearer ${userToken}`
        }
      }
    );
    const wallets = await response.json();
    setLinkedWallets(wallets.sort((a, b) => a.priority - b.priority));
  };

  const updatePriority = async (walletId, newPriority) => {
    setLoading(true);

    const updatedWallets = linkedWallets.map(w =>
      w.id === walletId ? { ...w, priority: newPriority } : w
    );

    // Reorder and reassign priorities
    const sorted = updatedWallets
      .sort((a, b) => a.priority - b.priority)
      .map((w, index) => ({ ...w, priority: index + 1 }));

    const payload = {
      wallets: sorted.map(w => ({
        addressId: w.address,
        priority: w.priority
      }))
    };

    await fetch(
      'https://api.example.com/v1/wallet/internal/card_linked/priority',
      {
        method: 'PUT',
        headers: {
          'x-client-key': 'YOUR_PUBLIC_KEY',
          'Authorization': `Bearer ${userToken}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(payload)
      }
    );

    await fetchLinkedWallets();
    setLoading(false);
  };

  return (
    <div className="priority-manager">
      <h3>Linked Wallets (Payment Priority)</h3>
      <p>Lower numbers are charged first</p>

      {linkedWallets.map(wallet => (
        <div key={wallet.id} className="wallet-row">
          <span className="priority">#{wallet.priority}</span>
          <div className="wallet-info">
            <strong>{wallet.currency.toUpperCase()}</strong>
            <span>{wallet.network}</span>
            <code>{wallet.address.slice(0, 10)}...</code>
          </div>
          <div className="priority-controls">
            <button
              onClick={() => updatePriority(wallet.id, wallet.priority - 1)}
              disabled={wallet.priority === 1 || loading}
            >

            </button>
            <button
              onClick={() => updatePriority(wallet.id, wallet.priority + 1)}
              disabled={wallet.priority === linkedWallets.length || loading}
            >

            </button>
          </div>
        </div>
      ))}
    </div>
  );
}

Transaction Flow Example

Here’s what happens when a user makes a $100 purchase with multiple linked wallets: Linked Wallets:
  • Priority 1: USDC on Linea (balance: $50)
  • Priority 2: USDT on Ethereum (balance: $75)
  • Priority 3: USDC on Solana (balance: $200)
Transaction Flow:
1

Check Priority 1

Platform checks USDC Linea wallet. Balance: 50.Insufficientfor50. Insufficient for 100 purchase.
2

Check Priority 2

Platform checks USDT Ethereum wallet. Balance: 75.Insufficientfor75. Insufficient for 100 purchase.
3

Check Priority 3

Platform checks USDC Solana wallet. Balance: 200.Sufficient!Deduct200. Sufficient! Deduct 100.
4

Complete Transaction

Card transaction approved. User’s Solana wallet balance now $100.
The platform checks wallets sequentially until finding one with sufficient balance. Only one wallet is charged per transaction.

Error Handling

Maximum Linked Wallets Exceeded

{
  "error": "Maximum linked wallets",
  "code": "WALLET_MAX_LINKED",
  "message": "User has reached the maximum number of linked wallets (5)"
}
Solution: Unlink an existing wallet before linking a new one.

Wallet Already Linked

{
  "error": "Wallet already linked",
  "code": "WALLET_ALREADY_LINKED",
  "message": "This wallet is already linked to the card"
}
Solution: No action needed. Wallet is already available for card payments.

Invalid Address ID

{
  "error": "Invalid address ID",
  "code": "WALLET_INVALID_ADDRESS_ID",
  "message": "The specified addressId does not exist or does not belong to this user"
}
Solution: Verify the addressId from GET /v1/wallet/internal response and ensure it belongs to the authenticated user.

Insufficient Balance (During Transaction)

{
  "error": "Insufficient balance",
  "code": "WALLET_INSUFFICIENT_BALANCE",
  "message": "All linked wallets have insufficient balance for this transaction"
}
Solution: Prompt user to fund one of their linked wallets or add a new wallet with sufficient balance.

Best Practices

Set Logical Priority

Prioritize wallets based on fees, balances, or user preferences. Lower-fee networks should typically have higher priority.

Display Balance Status

Show wallet balances in your UI so users understand which wallet will be charged and whether they have sufficient funds.

Allow Easy Reordering

Provide drag-and-drop or button controls to let users easily adjust wallet priority.

Show Network Fees

Display typical network fees for each wallet to help users make informed priority decisions.

Notify on Insufficient Balance

Alert users when their primary wallet has low balance and suggest funding it or adjusting priority.

Limit Linked Wallets

Consider encouraging users to link only 2-3 wallets to keep the experience simple and manageable.

Next Steps