curl --request POST \
--url https://api.starkfi.io/kyc/prepare-wallet \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api_key>' \
--data '{
"wallet": "FmTGYpzX27fDqaiytXUdFVaphC5o68G61Q3uhVM2d8bm",
"message": "<challenge_message>",
"signed_bytes": "<signature>"
}'
{
"statusCode": 201,
"success": true,
"status": "user_prepared",
"message": "User prepared for KYC via wallet",
"data": {
"email_verified": false,
"wallet_verified": true,
"kyc_approved": false,
"kyc_status": "pending"
}
}
{
"statusCode": 200,
"success": true,
"status": "user_already_prepared",
"message": "User prepared for KYC via wallet",
"data": {
"email_verified": false,
"wallet_verified": true,
"kyc_approved": false,
"kyc_status": "pending"
}
}
{
"statusCode": 401,
"success": false,
"status": "invalid_signature",
"message": "KYC wallet prepare failed"
}
{
"statusCode": 409,
"success": false,
"status": "wallet_already_linked",
"message": "KYC wallet prepare failed"
}
KYC
Prepare KYC (wallet)
Register a user for KYC by proving wallet ownership with a signed challenge message.
POST
/
kyc
/
prepare-wallet
curl --request POST \
--url https://api.starkfi.io/kyc/prepare-wallet \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api_key>' \
--data '{
"wallet": "FmTGYpzX27fDqaiytXUdFVaphC5o68G61Q3uhVM2d8bm",
"message": "<challenge_message>",
"signed_bytes": "<signature>"
}'
{
"statusCode": 201,
"success": true,
"status": "user_prepared",
"message": "User prepared for KYC via wallet",
"data": {
"email_verified": false,
"wallet_verified": true,
"kyc_approved": false,
"kyc_status": "pending"
}
}
{
"statusCode": 200,
"success": true,
"status": "user_already_prepared",
"message": "User prepared for KYC via wallet",
"data": {
"email_verified": false,
"wallet_verified": true,
"kyc_approved": false,
"kyc_status": "pending"
}
}
{
"statusCode": 401,
"success": false,
"status": "invalid_signature",
"message": "KYC wallet prepare failed"
}
{
"statusCode": 409,
"success": false,
"status": "wallet_already_linked",
"message": "KYC wallet prepare failed"
}
Use this path when your users connect a wallet instead of verifying by email OTP. The wallet must sign the challenge from Wallet challenge.
After a successful prepare, call Create KYC session with
string
required
Your StarkFi API key.
string
required
Wallet address that signed the challenge.
string
required
Exact challenge message returned by
POST /security/wallet/challenge.string
required
Signature bytes (base58 for Solana, hex for EVM) proving ownership of
wallet.string
Optional email to link to the wallet. If provided, it must not already belong to a different wallet.
curl --request POST \
--url https://api.starkfi.io/kyc/prepare-wallet \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api_key>' \
--data '{
"wallet": "FmTGYpzX27fDqaiytXUdFVaphC5o68G61Q3uhVM2d8bm",
"message": "<challenge_message>",
"signed_bytes": "<signature>"
}'
{
"statusCode": 201,
"success": true,
"status": "user_prepared",
"message": "User prepared for KYC via wallet",
"data": {
"email_verified": false,
"wallet_verified": true,
"kyc_approved": false,
"kyc_status": "pending"
}
}
{
"statusCode": 200,
"success": true,
"status": "user_already_prepared",
"message": "User prepared for KYC via wallet",
"data": {
"email_verified": false,
"wallet_verified": true,
"kyc_approved": false,
"kyc_status": "pending"
}
}
{
"statusCode": 401,
"success": false,
"status": "invalid_signature",
"message": "KYC wallet prepare failed"
}
{
"statusCode": 409,
"success": false,
"status": "wallet_already_linked",
"message": "KYC wallet prepare failed"
}
Signing the challenge message
After you receivedata.message from Wallet challenge, sign that exact string with the user’s wallet. Send the result as signed_bytes in this request (hex for EVM, base58 for Solana).
- EVM (ethers.js)
- Solana (web3.js)
import { Wallet } from "ethers";
const privateKey = "YOUR_PRIVATE_KEY";
const wallet = new Wallet(privateKey);
// Exact string from POST /security/wallet/challenge → data.message
const message =
"Prepare KYC StarkFi:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb at 1710000000000 nonce: a1b2c3... ";
const signed_bytes = await wallet.signMessage(message);
await fetch("https://api.starkfi.io/kyc/prepare-wallet", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": "<api_key>",
},
body: JSON.stringify({
wallet: wallet.address,
message,
signed_bytes,
}),
});
import { Keypair } from "@solana/web3.js";
import bs58 from "bs58";
import nacl from "tweetnacl";
const privateKeyBase58 = "YOUR_PRIVATE_KEY_BASE58";
const keypair = Keypair.fromSecretKey(bs58.decode(privateKeyBase58));
// Exact string from POST /security/wallet/challenge → data.message
const message =
"Prepare KYC StarkFi:FmTGYpzX27fDqaiytXUdFVaphC5o68G61Q3uhVM2d8bm at 1710000000000 nonce: a1b2c3... ";
const messageBytes = new TextEncoder().encode(message);
const signature = nacl.sign.detached(messageBytes, keypair.secretKey);
const signed_bytes = bs58.encode(signature);
await fetch("https://api.starkfi.io/kyc/prepare-wallet", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": "<api_key>",
},
body: JSON.stringify({
wallet: keypair.publicKey.toBase58(),
message,
signed_bytes,
}),
});
In the browser, use your wallet adapter’s
signMessage (EVM: personal_sign via ethers or the provider; Solana: sign the UTF-8 bytes and base58-encode the signature). With a provider like Privy, you can sign without exposing private keys in your app.wallet instead of email..png?fit=max&auto=format&n=I_v6ul4JDafNKFQP&q=85&s=f0ad25536f8a3ca42331fcff88ab2b26)