This page has been updated for Wallets SDK V1. If you are using the previous version,
see the previous version of this page or the V1 migration guide.
Prerequisites
- Ensure you have a wallet created.
- Signer: The signing wallet must be registered as a signer on the wallet. You can do this at wallet creation by passing
signers, or afterward by adding a signer. - API Key: Ensure you have an API key with the scopes:
wallets:transactions.create.
What is sending a custom transaction?
Sending a custom transaction lets you interact with any smart contract on the blockchain beyond simple transfers. Common use cases include minting free tokens, claiming rewards, or registering for allowlists—all without needing to manage private keys yourself.Sending a Transaction
- React
- Node.js
- React Native
- Swift
- REST
import { useWallet, EVMWallet } from '@crossmint/client-sdk-react-ui';
const { wallet } = useWallet();
const evmWallet = EVMWallet.from(wallet);
// Replace with the target contract address
const TARGET_CONTRACT = "0x...";
// Replace with the calldata for your contract interaction
const CALL_DATA = "0x...";
const { hash, explorerLink } = await evmWallet.sendTransaction({
to: TARGET_CONTRACT,
value: BigInt(0),
data: CALL_DATA,
});
import { CrossmintWallets, createCrossmint, EVMWallet } from "@crossmint/wallets-sdk";
const SERVER_API_KEY = process.env.SERVER_API_KEY!;
const CROSSMINT_SIGNER_SECRET = process.env.CROSSMINT_SIGNER_SECRET!;
// Replace with your wallet address
const WALLET_ADDRESS = "0x...";
// Replace with the target contract address
const TARGET_CONTRACT = "0x...";
// Replace with the calldata for your contract interaction
const CALL_DATA = "0x...";
const crossmint = createCrossmint({
apiKey: SERVER_API_KEY,
});
const crossmintWallets = CrossmintWallets.from(crossmint);
const wallet = await crossmintWallets.getWallet(
WALLET_ADDRESS,
{ chain: "base-sepolia" }
);
await wallet.useSigner({ type: "server", secret: CROSSMINT_SIGNER_SECRET });
const evmWallet = EVMWallet.from(wallet);
try {
const { hash, explorerLink } = await evmWallet.sendTransaction({
to: TARGET_CONTRACT,
value: BigInt(0),
data: CALL_DATA,
});
console.log("Transaction sent:", hash, explorerLink);
} catch (error) {
console.error("Failed to send transaction:", error);
throw error;
}
import { useWallet, EVMWallet } from '@crossmint/client-sdk-react-native-ui';
const { wallet } = useWallet();
const evmWallet = EVMWallet.from(wallet);
// Replace with the target contract address
const TARGET_CONTRACT = "0x...";
// Replace with the calldata for your contract interaction
const CALL_DATA = "0x...";
const { hash, explorerLink } = await evmWallet.sendTransaction({
to: TARGET_CONTRACT,
value: BigInt(0),
data: CALL_DATA,
});
import CrossmintClient
import Wallet
let sdk = CrossmintSDK.shared
let wallet = try await sdk.crossmintWallets.getWallet(
chain: .baseSepolia
)
let evmWallet = try EVMWallet.from(wallet: wallet)
let result = try await evmWallet.sendTransaction(transaction)
Parameters
The transaction to send.
Returns
The hash of the transaction.
The explorer link of the transaction.
Transactions must be approved by one of the wallet’s signers.
The SDK handles this automatically, but with the REST API you must approve the transaction to complete it.Call the approve transaction endpoint with the signature from Step 2 and the transaction ID from Step 1.See the API reference for more details.
Create the transaction
Call the create transaction endpoint.See the API reference for more details.
curl --request POST \
--url https://staging.crossmint.com/api/2025-06-09/wallets/<wallet-locator>/transactions \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: YOUR_API_KEY' \
--data '{
"params": {
"calls": [{
"to": "0x...",
"value": "0x",
"data": "0x..."
}],
"chain": "base-sepolia",
"signer": "server:YOUR_SERVER_SIGNER_ADDRESS"
}
}'
Choose your signer type
The next steps depend on which signer type you specified in the previous step.
- Server Signer
- Device Signer
- External Wallet
- Email & Phone
- Passkey
Server signers derive a private key from your signer secret using HKDF-SHA256 and sign transactions locally.When using the SDK, signing and approval are handled automatically — no additional steps are needed.When using the REST API directly, you must:See the Server Signer guide for full configuration and setup details.
- Derive the signing key from your signer secret (scoped to your project, environment, and chain). See the derive-server-signer helper for a reference implementation.
- Sign the approval message returned from the transaction creation step.
- Submit the signature via the approve transaction endpoint.
Crossmint recommends using the Wallets SDK for server signer flows. The SDK handles key derivation and signing automatically.
Device signers generate a P256 keypair inside the device’s secure hardware (Secure Enclave, Android Keystore, or browser Web Crypto API). Because the private key never leaves the device, device signers are client-side only and cannot be used directly with the REST API.See the Device Signer guide for setup instructions.
Crossmint recommends using the React, React Native, or Swift SDK for device signer flows. The SDK handles device key generation, storage, and signing automatically.
For External Wallet signers, you must manually sign the approval message and submit it via the API. The response from Step 1 includes a pending approval with a
message field that must be signed exactly as returned.From the previous step’s response, extract:id- The transaction ID (used in the next step)approvals.pending[0].message- The hex message to sign
import { privateKeyToAccount } from "viem/accounts";
// The message from tx.approvals.pending[0].message
const messageToSign = "<messageFromResponse>";
// Sign the message exactly as returned (raw hex)
const account = privateKeyToAccount(`0x${"<privateKey>"}`);
const signature = await account.signMessage({
message: { raw: messageToSign },
});
Email and phone signers require client-side OTP verification and key derivation, which the Crossmint SDK handles automatically. While REST API signing is technically possible, Crossmint does not recommend it because you would still need client-side SDK integration for the signing step.
Crossmint recommends using the React, React Native, Swift, or Node.js SDK examples instead. The SDK handles the full signing flow for email and phone signers.
Passkey signers use WebAuthn for biometric or password manager authentication, which requires browser interaction. While REST API signing is technically possible, Crossmint does not recommend it because you would still need client-side SDK integration for the WebAuthn signing step.
Crossmint recommends using the React, React Native, Swift, or Node.js SDK examples instead. The SDK handles the full passkey signing flow automatically.
Submit the approval
Skip this step if using the SDK with a server signer — the SDK handles signing and approval automatically.
curl --request POST \
--url https://staging.crossmint.com/api/2025-06-09/wallets/<walletAddress>/transactions/<txId>/approvals \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: <x-api-key>' \
--data '{
"approvals": [{
"signer": "external-wallet:<externalWalletAddress>",
"signature": "<signature>"
}]
}'

