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

# Headless crypto deposits

> Create crypto deposit accounts and fetch indicative quotes with useHeadlessCryptoDeposit in React

export const ChainIdentifier = () => <>
    Chain alias or CAIP-2 identifier, such as <code>base</code> or <code>eip155:8453</code>.
  </>;

export const AssetIdentifier = () => <>
    Asset alias or token contract address, such as <code>usdc</code> or{' '}
    <code>0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913</code>.
  </>;

Privy provides a `useHeadlessCryptoDeposit` hook that creates [crypto deposit accounts](/wallets/funding/crypto-deposits/overview) and fetches indicative quotes. The hook does not render the Privy modal. The app owns the UI.

This hook is React-only and is not available in React Native or Expo.

<Warning>
  `useHeadlessCryptoDeposit` is experimental. Import it from `@privy-io/react-auth/internal`. The
  interface may change without a major SDK version bump.
</Warning>

`createCryptoDepositAccount` requires an authenticated user and a dest-owner authorization signature. The hook signs that request with the user's signer.

## Prerequisites

Enable swaps and app-pays gas sponsorship as described in [setup](/wallets/funding/crypto-deposits/setup).

## Access the hook

Import and initialize `useHeadlessCryptoDeposit`:

```tsx {skip-check} theme={"system"}
import {useHeadlessCryptoDeposit} from '@privy-io/react-auth/internal';

const {createCryptoDepositAccount, getQuote} = useHeadlessCryptoDeposit();
```

## Create a deposit account

Call `createCryptoDepositAccount` with the destination wallet and an inline route (or an existing deposit configuration).

```tsx {skip-check} theme={"system"}
const {deposit_accounts} = await createCryptoDepositAccount({
  walletId: '<wallet-id>',
  type: 'inline_route',
  source: {
    mode: 'include',
    values: [{asset: 'usdc', chain: 'base'}]
  },
  destination: {asset: 'pathusd', chain: 'tempo'}
});
```

Your app can display each `deposit_address`. The same address is reusable for later deposits on that route.

For Node.js and REST, see [Create a deposit account](/wallets/funding/crypto-deposits/create-deposit-account).

### Parameters

`createCryptoDepositAccount` accepts an object with the following fields:

<ParamField body="walletId" type="string" required>
  Destination wallet to attach the deposit account to.
</ParamField>

<ParamField body="type" type="'inline_route' | 'deposit_config'" required>
  Which create payload to send. `inline_route` takes `source` and `destination`. `deposit_config`
  reuses an existing deposit configuration via `depositConfigId`.
</ParamField>

<ParamField body="source" type="object">
  Required when `type` is `inline_route`. Assets the deposit address accepts. Chains must be EVM or
  Solana.

  <Expandable title="properties" defaultOpen>
    <ParamField body="source.mode" type="'all' | 'include' | 'exclude'" required>
      `include` accepts only the listed assets. `exclude` accepts all except the listed assets.
      `all` accepts every supported source.
    </ParamField>

    <ParamField body="source.values" type="object[]">
      Asset specs for `include` and `exclude`. Omit when `mode` is `all`.

      <Expandable title="properties" defaultOpen>
        <ParamField body="source.values.asset" type="string">
          <AssetIdentifier />
        </ParamField>

        <ParamField body="source.values.chain" type="string">
          <ChainIdentifier /> Omit to match every supported chain for that asset.
        </ParamField>
      </Expandable>
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="destination" type="object">
  Required when `type` is `inline_route`. Asset delivered to the destination wallet. Identifies
  exactly one asset on exactly one chain.

  <Expandable title="properties" defaultOpen>
    <ParamField body="destination.asset" type="string" required>
      <AssetIdentifier />
    </ParamField>

    <ParamField body="destination.chain" type="string" required>
      <ChainIdentifier /> Must match the destination wallet's `chain_type`.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="depositConfigId" type="string">
  Required when `type` is `deposit_config`. ID of an existing deposit configuration to attach.
</ParamField>

### Return value

`createCryptoDepositAccount` returns a Promise for `{deposit_accounts}`. Each entry matches the [create response](/wallets/funding/crypto-deposits/create-deposit-account#response).

## Get a quote

Call `getQuote` with a concrete source and destination pair. Quotes do not require a wallet ID or authorization signature.

```tsx {skip-check} theme={"system"}
const quote = await getQuote({
  source: {chain: 'base', asset: 'usdc'},
  destination: {chain: 'tempo', asset: 'pathusd'},
  inputAmount: '1.5',
  slippageBps: 50
});
```

This calls `POST /v1/deposit_accounts/crypto/quote`.

### Parameters

`getQuote` accepts an object with the following fields:

<ParamField body="source" type="object" required>
  Asset the quote prices as input. Unlike create, both `chain` and `asset` are required.

  <Expandable title="properties" defaultOpen>
    <ParamField body="source.chain" type="string" required>
      <ChainIdentifier /> A quote is a specific route, so chain cannot be omitted.
    </ParamField>

    <ParamField body="source.asset" type="string" required>
      <AssetIdentifier />
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="destination" type="object" required>
  Asset delivered to the destination wallet.

  <Expandable title="properties" defaultOpen>
    <ParamField body="destination.chain" type="string" required>
      <ChainIdentifier />
    </ParamField>

    <ParamField body="destination.asset" type="string" required>
      <AssetIdentifier />
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="inputAmount" type="string">
  Amount as a decimal string in the source token's standard unit (for example, `"1.5"` for 1.5
  USDC). Not the smallest on-chain unit. Omit to quote approximately \$50 of the source asset.
</ParamField>

<ParamField body="slippageBps" type="number">
  Slippage tolerance in basis points.
</ParamField>

### Return value

`getQuote` returns a Promise with the following fields:

<ResponseField name="input_amount" type="string">
  Quoted input as a decimal string in the source token's standard unit.
</ResponseField>

<ResponseField name="estimated_output_amount" type="string">
  Estimated output as a decimal string in the destination token's standard unit.
</ResponseField>

<ResponseField name="created_at" type="string">
  ISO 8601 timestamp when the quote was created.
</ResponseField>

## Error handling

`createCryptoDepositAccount` rejects on invalid configuration or failed requests. Common error cases include:

* the user is not authenticated
* the dest-owner request expires before it is sent
* swaps or app-pays gas sponsorship are not enabled for the source chain
* the route is unsupported

`getQuote` rejects when the route is unsupported or source and destination are not both mainnet or both testnet.

Your app should wrap calls in `try/catch` and show clear UI feedback.

## Complete example

```tsx {skip-check} theme={"system"}
import {useState} from 'react';
import {useHeadlessCryptoDeposit} from '@privy-io/react-auth/internal';

export const CryptoDepositPanel = ({walletId}: {walletId: string}) => {
  const {createCryptoDepositAccount, getQuote} = useHeadlessCryptoDeposit();
  const [depositAddress, setDepositAddress] = useState<string | null>(null);
  const [estimatedOut, setEstimatedOut] = useState<string | null>(null);
  const [isLoading, setIsLoading] = useState(false);

  const onCreate = async () => {
    setIsLoading(true);
    try {
      const [{deposit_accounts}, quote] = await Promise.all([
        createCryptoDepositAccount({
          walletId,
          type: 'inline_route',
          source: {
            mode: 'include',
            values: [{asset: 'usdc', chain: 'base'}]
          },
          destination: {asset: 'pathusd', chain: 'tempo'}
        }),
        getQuote({
          source: {chain: 'base', asset: 'usdc'},
          destination: {chain: 'tempo', asset: 'pathusd'},
          inputAmount: '25'
        })
      ]);

      setDepositAddress(deposit_accounts[0]?.deposit_address ?? null);
      setEstimatedOut(quote.estimated_output_amount);
    } catch (error) {
      console.error(error);
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <div>
      <button type="button" onClick={onCreate} disabled={isLoading}>
        {isLoading ? 'Creating...' : 'Get deposit address'}
      </button>
      {depositAddress && (
        <p>
          Send USDC on Base to <code>{depositAddress}</code>
          {estimatedOut ? ` for about ${estimatedOut} pathUSD` : ''}
        </p>
      )}
    </div>
  );
};
```

## Related

<CardGroup cols={2}>
  <Card title="Create a deposit account" icon="arrow-right-to-bracket" href="/wallets/funding/crypto-deposits/create-deposit-account">
    Node.js and REST for the same create endpoint
  </Card>

  <Card title="Setup" icon="gear" href="/wallets/funding/crypto-deposits/setup">
    Enable swaps and app-pays gas sponsorship
  </Card>

  <Card title="Onramp modal" icon="window-restore" href="/wallets/funding/add-funds">
    Privy-hosted funding UI with `useAddFunds`
  </Card>
</CardGroup>
