Skip to main content
When onramping to non-Crossmint wallets, the onramp may require the user to prove they own the recipient wallet by signing a message. This is a legal requirement under anti-money laundering (AML) and counter-terrorism financing (CTF) regulations, which mandate verification of the beneficiary wallet owner’s identity above certain transaction thresholds. This page explains when proof of ownership is required and how it works.

When Is Proof of Ownership Required?

Proof of ownership is triggered based on transaction volume thresholds:
ConditionVerification Required?
Transaction below $1,000 AND 30-day volume below $1,000No
Transaction above $1,000 OR 30-day volume above $1,000Yes
Proof of ownership only applies to external (non-Crossmint) wallets. A Crossmint-managed wallet does not require ownership verification as long as it was created with its owner set to the user. A Crossmint wallet created without an owner (or owned by someone else) is treated as external and must be linked and verified like any other external wallet.
Wallet-ownership proof applies only to users verified outside the United States. Buyers whose KYC country of residence is the United States are not asked to prove wallet ownership, regardless of transaction amount.

How It Works

1

Link External Wallet

Before creating an onramp order to an external wallet, link the wallet to a Crossmint user (created automatically if one does not yet exist for the locator) using the Link External Wallet API:
curl --request PUT \
    --url https://staging.crossmint.com/api/2025-06-09/users/YOUR_USER_LOCATOR/linked-wallets/YOUR_WALLET_ADDRESS \
    --header 'X-API-KEY: YOUR_API_KEY' \
    --header 'Content-Type: application/json' \
    --data '{
        "chain": "base-sepolia"
    }'
The API responds with a verification challenge following the CAIP-122 standard:
{
    "address": "0x1234...5678",
    "chain": "base-sepolia",
    "type": "external-wallet",
    "ownership": {
        "verified": false,
        "verificationChallenge": "crossmint.com wants you to sign in with your blockchain account:\n0x1234...5678\n\n..."
    }
}
2

Create Order

Create the onramp order as usual. If verification is required, the order response will have status requires-recipient-verification:
{
    "order": {
        "payment": {
            "status": "requires-recipient-verification",
            "preparation": {
                "message": "crossmint.com wants you to sign in with your blockchain account:\n0x1234...5678\n\n..."
            }
        }
    }
}
3

Sign the Message

Have the user sign the preparation.message using their wallet’s private key:
import { Wallet } from "ethers";

const wallet = new Wallet("YOUR_PRIVATE_KEY");
const message = order.payment.preparation.message;
const signature = await wallet.signMessage(message);
4

Submit Signature

Submit the signature as proof of ownership using the same Link External Wallet API:
curl --request PUT \
    --url https://staging.crossmint.com/api/2025-06-09/users/YOUR_USER_LOCATOR/linked-wallets/YOUR_WALLET_ADDRESS \
    --header 'X-API-KEY: YOUR_API_KEY' \
    --header 'Content-Type: application/json' \
    --data '{
        "chain": "base-sepolia",
        "proof": "YOUR_SIGNATURE"
    }'
Once verified, the response confirms ownership:
{
    "address": "0x1234...5678",
    "chain": "base-sepolia",
    "type": "external-wallet",
    "ownership": {
        "verified": true
    }
}
5

Complete Order

After ownership is verified, fetch the order using the Get Order API. The status will advance to:
  • requires-kyc — the user has not completed KYC yet.
  • awaiting-payment — the user can proceed to payment.

Pre-Verifying Ownership

You can verify wallet ownership at the time of linking (before any order is created) by including the proof field in the initial Link External Wallet call. This avoids requiring verification during the checkout flow.
curl --request PUT \
    --url https://staging.crossmint.com/api/2025-06-09/users/YOUR_USER_LOCATOR/linked-wallets/YOUR_WALLET_ADDRESS \
    --header 'X-API-KEY: YOUR_API_KEY' \
    --header 'Content-Type: application/json' \
    --data '{
        "chain": "base-sepolia",
        "proof": "YOUR_SIGNATURE"
    }'