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

# Onramp to Non-Crossmint Wallets

> Enable onramp orders to wallets not created by Crossmint

This guide walks you through how to enable onramp orders to external wallets (EOAs or any other smart wallet from a different wallet provider).

<Steps>
  <Step title="Link External Wallet to Crossmint User">
    Before creating an onramp order to an external wallet, you must link the wallet to a Crossmint user, using the Link External Wallet API:

    <CodeGroup>
      ```bash cURL theme={null}
      curl --request PUT \
          --url https://staging.crossmint.com/api/2025-06-09/users/{userLocator}/linked-wallets/{walletAddress} \
          --header 'X-API-KEY: <x-api-key>' \
          --header 'Content-Type: application/json' \
          --data '{
              "chain": "base-sepolia",
              "proof": "<signature>"
          }'
      ```

      ```js Node.js theme={null}
      const options = {
          method: 'PUT',
          headers: {
              'X-API-KEY': '<x-api-key>',
              'Content-Type': 'application/json'
          },
          body: JSON.stringify({
              chain: "base-sepolia",
              proof: "<signature>"
          })
      };

      const userLocator = "email:user@example.com";
      const walletAddress = "0x1234567890abcdef1234567890abcdef12345678";

      fetch(`https://staging.crossmint.com/api/2025-06-09/users/${userLocator}/linked-wallets/${walletAddress}`, options)
          .then(response => response.json())
          .then(response => console.log(response))
          .catch(err => console.error(err));
      ```
    </CodeGroup>

    The `proof` field is optional:

    * If you omit it, you are simply linking the external wallet to the user without proving ownership yet.
    * If you pass it, you prove ownership of the wallet in the same request.

    When linking without proof, the API responds with a verification challenge:

    ```json theme={null}
    {
        "address": "0x1234567890abcdef1234567890abcdef12345678",
        "chain": "base-sepolia",
        "type": "external-wallet",
        "ownership": {
            "verified": false,
            "verificationChallenge": "crossmint.com wants you to sign in with your blockchain account:\n0x1234...5678\n\nI am signing this message to prove ownership of my wallet address 0x1234...5678 for Crossmint verification.\n\nURI: https://..."
        }
    }
    ```

    The `verificationChallenge` follows the [CAIP-122](https://chainagnostic.org/CAIPs/caip-122) standard.
  </Step>

  <Step title="Create Onramp Order">
    Create an onramp order using the [Create Order API](/api-reference/headless/create-order) with the linked external wallet address as the recipient.

    <CodeGroup>
      ```bash cURL theme={null}
      curl --request POST \
          --url https://staging.crossmint.com/api/2022-06-09/orders \
          --header 'X-API-KEY: <x-api-key>' \
          --header 'Content-Type: application/json' \
          --data '{
              "recipient": {
                  "walletAddress": "0x1234567890abcdef1234567890abcdef12345678"
              },
              "payment": {
                  "method": "card",
                  "receiptEmail": "user@example.com"
              },
              "lineItems": [
                  {
                      "tokenLocator": "base-sepolia:0x036CbD53842c5426634e7929541eC2318f3dCF7e",
                      "executionParameters": {
                          "mode": "exact-in",
                          "amount": "10"
                      }
                  }
              ]
          }'
      ```

      ```js Node.js theme={null}
      const options = {
          method: 'POST',
          headers: {
              'X-API-KEY': '<x-api-key>',
              'Content-Type': 'application/json'
          },
          body: JSON.stringify({
              recipient: {
                  walletAddress: "0x1234567890abcdef1234567890abcdef12345678"
              },
              payment: {
                  method: "card",
                  receiptEmail: "user@example.com"
              },
              lineItems: [
                  {
                      tokenLocator: "base-sepolia:0x036CbD53842c5426634e7929541eC2318f3dCF7e",
                      executionParameters: {
                          mode: "exact-in",
                          amount: "10"
                      }
                  }
              ]
          })
      };

      fetch('https://staging.crossmint.com/api/2022-06-09/orders', options)
          .then(response => response.json())
          .then(response => console.log(response))
          .catch(err => console.error(err));
      ```
    </CodeGroup>

    Depending on the transaction value and the user's past activity, the order may or may not require wallet ownership verification:

    * **Below threshold**: If the transaction value is below 1,000 euros **and** the user's past 30-day onramp transaction volume is below 1,000 euros, ownership verification is not required. The order proceeds directly to the kyc/payment phase.
    * **Above threshold**: If either threshold is surpassed, the order status will be `requires-recipient-verification` and the user must sign a message to prove wallet ownership.

    When ownership verification is required, the order response will look like this:

    ```json theme={null}
    {
        "clientSecret": "...",
        "order": {
            "orderId": "987e81ab-8c8f-464e-95e9-11ceda80d559",
            "phase": "payment",
            "lineItems": [...],
            "quote": {...},
            "payment": {
                "status": "requires-recipient-verification",
                "method": "card",
                "currency": "usd",
                "preparation": {
                    "message": "crossmint.com wants you to sign in with your blockchain account:\n0x1234...5678\n\nI am signing this message to prove ownership of my wallet address 0x1234...5678 for Crossmint verification.\n\nURI: https://...\nVersion: 1\nNonce: ...\nIssued At: ...\nExpiration Time: ...\nRequest ID: ...\nChain ID: base-sepolia",
                    "email": "user@example.com"
                }
            }
        }
    }
    ```
  </Step>

  <Step title="Sign Message">
    If the order requires ownership verification, the user must sign the `preparation.message` returned in the order response using their wallet's private key.

    <CodeGroup>
      ```js EVM (ethers.js) theme={null}
      import { Wallet } from "ethers";

      const PRIVATE_KEY = "YOUR_WALLET_PRIVATE_KEY"; // Replace with your wallet's private key
      const wallet = new Wallet(PRIVATE_KEY);
      const message = order.payment.preparation.message;
      const signature = await wallet.signMessage(message);
      ```

      ```js Solana (tweetnacl) theme={null}
      import nacl from "tweetnacl";
      import { decodeUTF8 } from "tweetnacl-util";
      import { encode as base64Encode } from "@stablelib/base64";

      const message = order.payment.preparation.message;
      const messageBytes = decodeUTF8(message);
      const signatureBytes = nacl.sign.detached(messageBytes, keypair.secretKey);
      const signature = base64Encode(signatureBytes);
      ```
    </CodeGroup>
  </Step>

  <Step title="Submit Signature">
    Use the same Link External Wallet API to submit the signature as proof of ownership. Pass the signature in the `proof` field:

    <CodeGroup>
      ```bash cURL theme={null}
      curl --request PUT \
          --url https://staging.crossmint.com/api/2025-06-09/users/{userLocator}/linked-wallets/{walletAddress} \
          --header 'X-API-KEY: <x-api-key>' \
          --header 'Content-Type: application/json' \
          --data '{
              "chain": "base-sepolia",
              "proof": "<signature>"
          }'
      ```

      ```js Node.js theme={null}
      const options = {
          method: 'PUT',
          headers: {
              'X-API-KEY': '<x-api-key>',
              'Content-Type': 'application/json'
          },
          body: JSON.stringify({
              chain: "base-sepolia",
              proof: "<signature>"
          })
      };

      const userLocator = "email:user@example.com";
      const walletAddress = "0x1234567890abcdef1234567890abcdef12345678";

      fetch(`https://staging.crossmint.com/api/2025-06-09/users/${userLocator}/linked-wallets/${walletAddress}`, options)
          .then(response => response.json())
          .then(response => console.log(response))
          .catch(err => console.error(err));
      ```
    </CodeGroup>

    Once ownership is verified, the response will confirm verification:

    ```json theme={null}
    {
        "address": "0x1234567890abcdef1234567890abcdef12345678",
        "chain": "base-sepolia",
        "type": "external-wallet",
        "ownership": {
            "verified": true
        }
    }
    ```
  </Step>

  <Step title="Complete Order">
    Once the signature is submitted and verified, fetch the order using the [Get Order API](/api-reference/headless/get-order). The order status should now have proceeded to the next phase:

    * **`requires-kyc`**: The user hasn't completed KYC yet and will be guided through the KYC flow first.
    * **`awaiting-payment`**: The user has completed KYC and can proceed to complete payment.

    The user will be guided through Crossmint's embedded components to complete their order, handling payment and any remaining compliance steps automatically.
  </Step>
</Steps>
