Kevo Docs

Quick Start

Get users signing in with Kevo in under 5 minutes.

1. Install the SDK

bash
npm install @kevo-ws/sdk

2. Get your API key

Sign in to the Kevo Portal. Create a project, configure your auth methods and supported chains, then copy the Publishable Key from the project settings.

The publishable key is safe to include in client-side code, it only identifies your project, it does not grant administrative access.

3. Wrap your app with KevoProvider

Add KevoProvider at the root of your React tree. Pass a config object with your publishable key and optional RPC URLs. This initializes the client, loads your project configuration, and makes all hooks available.

typescript
import { KevoProvider } from '@kevo-ws/sdk/react'

export default function App() {
  return (
    <KevoProvider
      config={{
        publishableKey: 'pk_live_xxxxxxxxxxxx',
        // Multi-chain EVM — map chainId → RPC URL
        defaultChainId: 1,
        chains: {
          1:    'https://eth-mainnet.infura.io/v3/YOUR_KEY',
          8453: 'https://base-mainnet.infura.io/v3/YOUR_KEY',
          137:  'https://polygon-mainnet.infura.io/v3/YOUR_KEY',
        },
        // Single-chain shorthand (use instead of chains for simple projects)
        // evmRpcUrl: 'https://eth.llamarpc.com',
        solanaRpcUrl: 'https://api.mainnet-beta.solana.com',
      }}
    >
      <YourApp />
    </KevoProvider>
  )
}
With chains configured, sendTransaction, useBalance, and estimateGas automatically pick the right RPC from tx.chainId or the active chain — no per-call config needed. Use useChain() to switch chains at runtime.

4. Add the sign-in modal

Place KevoModal anywhere inside the provider tree. It renders nothing until opened. Use useKevoModal() to control it and useKevo() for session state.

typescript
import { KevoModal, useKevoModal, useKevo, useUserProfile } from '@kevo-ws/sdk/react'

function Header() {
  const { open } = useKevoModal()
  const { isAuthenticated } = useKevo()
  const { profile } = useUserProfile()

  return (
    <header>
      {isAuthenticated ? (
        <span>
          <img src={profile.avatarUrl} alt="" />
          {profile.name}
        </span>
      ) : (
        <button onClick={open}>Sign In</button>
      )}
    </header>
  )
}

function RootLayout() {
  return (
    <>
      <KevoModal />
      <Header />
      {/* rest of your app */}
    </>
  )
}

4.1 Render user profile

Kevo exposes a normalized profile for display. The primary name is always the shortened wallet address. If the login provider returns a username/name or avatar, Kevo exposes it too; otherwise the SDK generates a deterministic avatar from the wallet address.

typescript
import { useUserProfile } from '@kevo-ws/sdk/react'

function UserChip() {
  const { profile, rawProfile } = useUserProfile()

  return (
    <div>
      <img src={profile.avatarUrl} alt="" />
      <strong>{profile.name}</strong>
      {profile.username && <span>{profile.username}</span>}
      {rawProfile?.email && <small>{rawProfile.email}</small>}
    </div>
  )
}
X returns username, name, and profile image, but not email. If the user needs to export their private key, use the built-in dashboard flow or useUserProfile() to link an email first.

5. Access the wallet and send transactions

After sign-in, use useWallets() to get wallet addresses, useSignTransaction() to sign & broadcast, and useBalance() for live balance display. The SDK auto-fills nonce, gas limit, and fee data — just specify to, value, and chainId.

typescript
import { useWallets, useSignTransaction, useBalance } from '@kevo-ws/sdk/react'
import { nativeTransfer } from '@kevo-ws/sdk/helpers'

function WalletView() {
  const { evmWallet } = useWallets()
  const { sendTransaction, isLoading } = useSignTransaction()
  const { balance } = useBalance() // auto-refreshes every 15s

  if (!evmWallet) return <p>No wallet yet</p>

  const handleSend = async () => {
    const tx = nativeTransfer({
      to: '0xRecipientAddress',
      value: 1_000_000_000_000_000n, // 0.001 ETH
      chainId: 1,
    })
    const txHash = await sendTransaction(tx)
    console.log('TX hash:', txHash)
  }

  return (
    <div>
      <p>Address: {evmWallet.address}</p>
      <p>Balance: {balance !== null
        ? (Number(balance) / 1e18).toFixed(6) + ' ETH'
        : 'Loading…'}</p>
      <button onClick={handleSend} disabled={isLoading}>
        {isLoading ? 'Sending…' : 'Send 0.001 ETH'}
      </button>
    </div>
  )
}

That's it

Your users can now sign in without passwords or seed phrases, and you can send transactions on their behalf with their approval — nonce, gas, and fees are handled automatically by the SDK.