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

# Prepare KYC (wallet)

> Register a user for KYC by proving wallet ownership with a signed challenge message.

Use this path when your users connect a wallet instead of verifying by email OTP. The wallet must sign the challenge from [Wallet challenge](/wallet-challenge).

<ParamField header="x-api-key" type="string" required placeholder="your-api-key">
  Your StarkFi API key.
</ParamField>

<ParamField body="wallet" type="string" required>
  Wallet address that signed the challenge.
</ParamField>

<ParamField body="message" type="string" required>
  Exact challenge message returned by `POST /security/wallet/challenge`.
</ParamField>

<ParamField body="signed_bytes" type="string" required>
  Signature bytes (base58 for Solana, hex for EVM) proving ownership of `wallet`.
</ParamField>

<ParamField body="email" type="string">
  Optional email to link to the wallet. If provided, it must not already belong to a different wallet.
</ParamField>

<RequestExample>
  ```bash cURL theme={null}
  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>"
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 201 - Created theme={null}
  {
    "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"
    }
  }
  ```

  ```json 200 - Already prepared theme={null}
  {
    "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"
    }
  }
  ```

  ```json 401 - Invalid signature theme={null}
  {
    "statusCode": 401,
    "success": false,
    "status": "invalid_signature",
    "message": "KYC wallet prepare failed"
  }
  ```

  ```json 409 - Conflict theme={null}
  {
    "statusCode": 409,
    "success": false,
    "status": "wallet_already_linked",
    "message": "KYC wallet prepare failed"
  }
  ```
</ResponseExample>

## Signing the challenge message

After you receive `data.message` from [Wallet challenge](/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).

<Tabs>
  <Tab title="EVM (ethers.js)">
    ```javascript theme={null}
    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,
      }),
    });
    ```
  </Tab>

  <Tab title="Solana (web3.js)">
    ```javascript theme={null}
    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,
      }),
    });
    ```
  </Tab>
</Tabs>

<Tip>
  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](https://privy.io), you can sign without exposing private keys in your app.
</Tip>

After a successful prepare, call [Create KYC session](/create-session-1) with `wallet` instead of `email`.
