Skip to main content
This guide walks you through how to enable onramp orders to external wallets.
1

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:
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>"
    }'
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:
{
    "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 standard.
2

Create Onramp Order

Create an onramp order using the Create Order API with the linked external wallet address as the recipient.
Depending on the transaction value and the user’s past activity, the order may require wallet ownership verification (steps 3 and 4). If verification is not required, the order proceeds directly to the KYC/payment phase and you can skip to step 5.
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"
                }
            }
        ]
    }'
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 (steps 3 and 4).
When ownership verification is required, the order response will look like this:
{
    "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"
            }
        }
    }
}
3

Sign Message (conditional)

This step is only required if the order status is requires-recipient-verification.
The user must sign the preparation.message returned in the order response using their wallet’s private key.
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);
4

Submit Signature (conditional)

This step is only required if the order status is requires-recipient-verification.
Use the same Link External Wallet API to submit the signature as proof of ownership. Pass the signature in the proof field:
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>"
    }'
Once ownership is verified, the response will confirm verification:
{
    "address": "0x1234567890abcdef1234567890abcdef12345678",
    "chain": "base-sepolia",
    "type": "external-wallet",
    "ownership": {
        "verified": true
    }
}
5

Complete Order

Once ownership is verified (or if verification was not required), fetch the order using the Get Order API. 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.