> ## Documentation Index
> Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# createCoinbaseExchangeTransfer

# createCoinbaseExchangeTransfer

Creates a transfer from a Coinbase exchange account to an external wallet address. This lets users withdraw cryptocurrency from their Coinbase holdings to a wallet they control.

Before calling this function, use [`getCoinbaseAccounts`](/docs/javascript/funding/get-coinbase-accounts) to get the `accountId` and check the available balance.

Coinbase transfers can involve two extra steps beyond a normal API call, covered in detail below:

* **Two-step verification (2FA)**: a one-time code Coinbase sends to the account holder.
* **Travel Rule information**: some recipient details Coinbase is legally required to collect for certain transfers.

## Usage

```javascript theme={"system"}
import {
  createCoinbaseExchangeTransfer,
  getCoinbaseAccounts,
} from '@dynamic-labs-sdk/client';

// Get the user's Coinbase accounts
const accounts = await getCoinbaseAccounts();

// Create a transfer from the first account. Include any recipient info you
// already have as travelRuleData. See below for why.
const transfer = await createCoinbaseExchangeTransfer({
  accountId: accounts[0].id,
  to: '0x742d35Cc6634C0532925a3b844Bc9e7595f7ABCD',
  amount: 0.5,
  currency: 'ETH',
  travelRuleData: { beneficiaryName: 'Jane Doe', beneficiaryCountry: 'US' },
});

console.log('Transfer ID:', transfer.id);
console.log('Status:', transfer.status);
```

## Parameters

| Parameter                 | Type                       | Description                                                                                                                                                                                    |
| ------------------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `accountId`               | `string`                   | The Coinbase account ID to transfer from. Get this from `getCoinbaseAccounts()`.                                                                                                               |
| `to`                      | `string`                   | The destination wallet address.                                                                                                                                                                |
| `amount`                  | `number`                   | The amount to transfer.                                                                                                                                                                        |
| `currency`                | `string`                   | The cryptocurrency to transfer (e.g., `'ETH'`, `'BTC'`, `'USDC'`).                                                                                                                             |
| `description`             | `string` (optional)        | A description for the transfer.                                                                                                                                                                |
| `network`                 | `string` (optional)        | Network name (e.g., `'ethereum'`, `'polygon'`).                                                                                                                                                |
| `networkObject`           | `object` (optional)        | Network details with `chainName` and `networkId`. Use this for multi-network tokens.                                                                                                           |
| `networkObject.chainName` | `string`                   | The chain name (e.g., `'EVM'`).                                                                                                                                                                |
| `networkObject.networkId` | `string`                   | The network ID (e.g., `'1'` for Ethereum mainnet, `'137'` for Polygon).                                                                                                                        |
| `destinationTag`          | `string` (optional)        | Destination tag/memo some chains require to route funds to the right sub-account (e.g. XRP, Stellar).                                                                                          |
| `mfaCode`                 | `string` (optional)        | The two-step verification code Coinbase sent the account holder. See [Two-step verification](#two-step-verification-2fa) below.                                                                |
| `travelRuleData`          | `object` (optional)        | Recipient information Coinbase may require by law. See [Travel Rule](#travel-rule-information) below. **Send this on every transfer whenever you have it**, not just after being asked for it. |
| `id`                      | `string` (optional)        | Idempotency key to prevent duplicate transfers on retry.                                                                                                                                       |
| `client`                  | `DynamicClient` (optional) | The Dynamic client instance. Only required when using multiple clients.                                                                                                                        |

## Returns

`Promise<ExchangeTransferResponse>` - A promise that resolves to the transfer details:

```typescript theme={"system"}
type ExchangeTransferResponse = {
  id: string;                  // Unique transfer ID
  exchangeAccountId?: string;  // The Coinbase account ID
  status?: string;             // Transfer status: 'pending', 'completed', 'failed', etc.
  amount: number;              // Amount transferred
  currency: string;            // Currency code
  createdAt?: Date;            // When the transfer was created
};
```

## Two-step verification (2FA)

Coinbase requires its own two-step verification for most transfers, separate from any authentication Dynamic handles. The first `createCoinbaseExchangeTransfer` call for a given transfer makes Coinbase automatically send the account holder a short-lived code (SMS, or a push to an authenticator app like Authy). The call throws `CoinbaseTransferMfaRequiredError`; retry the **same** transfer with `mfaCode` set to that code.

| Error                              | Meaning                                                                | What to do                                                                                                           |
| ---------------------------------- | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `CoinbaseTransferMfaRequiredError` | A code was just sent to the account holder.                            | Prompt the user for the code, then retry with `mfaCode` set.                                                         |
| `CoinbaseTransferMfaFailedError`   | The submitted `mfaCode` was wrong, or expired before it was submitted. | Retry without `mfaCode`. This makes Coinbase send a fresh code, triggering `CoinbaseTransferMfaRequiredError` again. |

```javascript theme={"system"}
import {
  createCoinbaseExchangeTransfer,
  CoinbaseTransferMfaRequiredError,
} from '@dynamic-labs-sdk/client';

const transferParams = {
  accountId,
  amount: 0.5,
  currency: 'USDC',
  to,
  // Send known recipient info upfront. See Travel Rule information below.
  travelRuleData: { beneficiaryName: 'Jane Doe', beneficiaryCountry: 'US' },
};

try {
  await createCoinbaseExchangeTransfer(transferParams);
} catch (error) {
  if (error instanceof CoinbaseTransferMfaRequiredError) {
    // Coinbase already sent a code to the account holder. Prompt for it.
    const mfaCode = await promptUserForCoinbase2faCode();
    await createCoinbaseExchangeTransfer({ ...transferParams, mfaCode });
  }
}
```

## Travel Rule information

The "Travel Rule" is a law that requires exchanges like Coinbase to know who is sending money to whom for larger or cross-border transfers, not just wallet addresses but basic recipient identity. Whether it applies, and which fields it needs, depends on the sender's and recipient's countries and the transfer amount, so it can't be predicted ahead of time. If Coinbase needs this information and it's missing, the call throws `CoinbaseTravelRuleRequiredError` listing exactly which fields are required, each with a description you can show the user.

```typescript theme={"system"}
type CoinbaseTravelRuleData = {
  beneficiaryWalletType?: 'WALLET_TYPE_SELF_HOSTED' | 'WALLET_TYPE_EXCHANGE'; // Is the destination wallet self-custody, or held at another exchange?
  isSelfTransfer?: 'IS_SELF_TRUE' | 'IS_SELF_FALSE'; // Is the destination wallet also the sender's own?
  beneficiaryName?: string;                 // Recipient's full legal name
  beneficiaryCountry?: string;               // Recipient's country, as an ISO 3166-1 alpha-2 code (e.g. "US")
  beneficiaryFinancialInstitution?: string;  // Recipient exchange's VASP ID (only needed if beneficiaryWalletType is 'WALLET_TYPE_EXCHANGE')
  beneficiaryAddress?: {
    address1?: string;
    city?: string;
    country?: string;
    postalCode?: string;
    state?: string;
  };
  transferPurpose?: string;                  // Free-text reason for the transfer (e.g. "Rent")
};
```

<Warning>
  Coinbase checks 2FA *before* Travel Rule on every request. If a transfer needs both and `travelRuleData` is missing on the attempt that finally includes a correct `mfaCode`, that attempt fails on the Travel Rule check instead of succeeding, and the `mfaCode` you just used is now spent, forcing the user through 2FA a second time. **Send `travelRuleData` upfront whenever you already have it** (e.g. a saved recipient), rather than waiting to be asked, so a transfer needing both only ever costs one 2FA prompt.
</Warning>

```javascript theme={"system"}
import {
  createCoinbaseExchangeTransfer,
  CoinbaseTravelRuleRequiredError,
} from '@dynamic-labs-sdk/client';

// Good: include any known recipient info from the start.
const transferParams = {
  accountId,
  amount: 0.5,
  currency: 'USDC',
  to,
  travelRuleData: { beneficiaryName: 'Jane Doe', beneficiaryCountry: 'US' },
};

try {
  await createCoinbaseExchangeTransfer(transferParams);
} catch (error) {
  if (error instanceof CoinbaseTravelRuleRequiredError) {
    // e.g. [{ name: 'BENEFICIARY_WALLET_TYPE', description: '...' }, ...]
    const travelRuleData = await promptUserForTravelRuleFields(error.missingFields);
    await createCoinbaseExchangeTransfer({
      ...transferParams,
      travelRuleData: { ...transferParams.travelRuleData, ...travelRuleData },
    });
  }
}
```

## Errors

| Error                              | When it's thrown                                                                                             |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `CoinbaseTransferMfaRequiredError` | A two-step verification code is required. See [Two-step verification](#two-step-verification-2fa).           |
| `CoinbaseTransferMfaFailedError`   | The submitted `mfaCode` was wrong or expired. See [Two-step verification](#two-step-verification-2fa).       |
| `CoinbaseTravelRuleRequiredError`  | Recipient information is missing for this transfer. See [Travel Rule information](#travel-rule-information). |
| `APIError`                         | Any other error returned by the transfer request (e.g. insufficient balance).                                |

## Examples

### Transfer USDC on a specific network

When transferring tokens that exist on multiple networks, specify the target network:

```javascript theme={"system"}
import { createCoinbaseExchangeTransfer } from '@dynamic-labs-sdk/client';

// Transfer USDC to Polygon
const transfer = await createCoinbaseExchangeTransfer({
  accountId: 'acc_123',
  to: '0x742d35Cc6634C0532925a3b844Bc9e7595f7ABCD',
  amount: 100,
  currency: 'USDC',
  networkObject: {
    chainName: 'EVM',
    networkId: '137', // Polygon
  },
  travelRuleData: { beneficiaryName: 'Jane Doe', beneficiaryCountry: 'US' },
});
```

### Idempotent transfers

Use an idempotency key to safely retry failed requests without creating duplicate transfers:

```javascript theme={"system"}
import { createCoinbaseExchangeTransfer } from '@dynamic-labs-sdk/client';
import { v4 as uuidv4 } from 'uuid';

const createSafeTransfer = async (params) => {
  const transfer = await createCoinbaseExchangeTransfer({
    ...params,
    id: uuidv4(), // Idempotency key
  });

  return transfer;
};

// If the network fails and you retry, the same transfer is returned
// instead of creating a duplicate
const transfer = await createSafeTransfer({
  accountId: 'acc_123',
  to: '0x742d35Cc6634C0532925a3b844Bc9e7595f7ABCD',
  amount: 0.5,
  currency: 'ETH',
  travelRuleData: { beneficiaryName: 'Jane Doe', beneficiaryCountry: 'US' },
});
```

### Complete transfer flow (2FA + Travel Rule)

```javascript theme={"system"}
import {
  createCoinbaseExchangeTransfer,
  CoinbaseTransferMfaRequiredError,
  CoinbaseTransferMfaFailedError,
  CoinbaseTravelRuleRequiredError,
} from '@dynamic-labs-sdk/client';

const executeTransfer = async (baseParams) => {
  let params = { ...baseParams };

  // Up to 3 attempts: the original request, plus one retry per extra
  // requirement Coinbase reports back.
  for (let attempt = 0; attempt < 3; attempt++) {
    try {
      return await createCoinbaseExchangeTransfer(params);
    } catch (error) {
      if (error instanceof CoinbaseTransferMfaRequiredError) {
        const mfaCode = await promptUserForCoinbase2faCode();
        params = { ...params, mfaCode };
      } else if (error instanceof CoinbaseTransferMfaFailedError) {
        // Wrong or expired code. Drop it so Coinbase sends a fresh one.
        const { mfaCode, ...rest } = params;
        params = rest;
      } else if (error instanceof CoinbaseTravelRuleRequiredError) {
        const travelRuleData = await promptUserForTravelRuleFields(
          error.missingFields
        );
        // Body changed. Coinbase invalidates any mfaCode issued for the
        // previous version of this request, so drop it too.
        const { mfaCode, ...rest } = params;
        params = {
          ...rest,
          travelRuleData: { ...rest.travelRuleData, ...travelRuleData },
        };
      } else {
        throw error;
      }
    }
  }

  throw new Error('Transfer did not complete after 3 attempts');
};

// Usage
const transfer = await executeTransfer({
  accountId: accounts[0].id,
  amount: 0.5,
  currency: 'ETH',
  to: '0x742d35Cc6634C0532925a3b844Bc9e7595f7ABCD',
  // Sending known recipient info upfront skips a Travel Rule round trip
  // entirely when it's the only extra thing this transfer needs.
  travelRuleData: { beneficiaryName: 'Jane Doe', beneficiaryCountry: 'US' },
});
```

### React component with transfer form

```javascript theme={"system"}
import { useState } from 'react';
import { createCoinbaseExchangeTransfer } from '@dynamic-labs-sdk/client';

const TransferButton = ({
  accountId,
  currency,
  destination,
  amount,
  recipientInfo, // Optional: { beneficiaryName, beneficiaryCountry, ... }
  onSuccess,
  onError,
}) => {
  const [submitting, setSubmitting] = useState(false);

  const handleTransfer = async () => {
    setSubmitting(true);

    try {
      const transfer = await createCoinbaseExchangeTransfer({
        accountId,
        to: destination,
        amount: parseFloat(amount),
        currency,
        travelRuleData: recipientInfo,
      });

      onSuccess(transfer);
    } catch (error) {
      onError(error);
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <button onClick={handleTransfer} disabled={submitting}>
      {submitting ? 'Processing...' : `Transfer ${amount} ${currency}`}
    </button>
  );
};
```

## Prerequisites

* User must have connected their Coinbase account through Dynamic
* The Coinbase account must have sufficient balance for the transfer

## Notes

* Transfers are processed by Coinbase and may take time to complete depending on network conditions
* The `status` field indicates the current state of the transfer
* Use idempotency keys (`id` parameter) in production to prevent duplicate transfers from network retries
* For tokens on multiple networks (like USDC), always specify the `networkObject` to ensure the transfer goes to the correct chain
* **Always send `travelRuleData` upfront when you already have the recipient's information.** It's optional, but supplying it late costs users an extra two-step verification prompt

## Related

* [`getCoinbaseAccounts`](/docs/javascript/funding/get-coinbase-accounts) - Get user's Coinbase account balances
