docs
Get startedTroubleshootingPaymentsWebhooksAgent paymentsSwop MCPOviIdentity (swop.id)SwivyRPC (preview)API reference

React quickstart

Email login, an embedded wallet, a passkey and a first transaction in a React app.

Preview: Swivy is not generally available

Swivy is in staging. It is not in production and Swop's own apps don't run on it yet. The SDK packages aren't published to npm; preview partners get access along with their credentials. Everything on this page can change. Request early access.

By the end of this page your React app will sign a user in with an email code, give them an EVM wallet, enroll a passkey and send a transaction approved by that passkey.

1. Install

npm install @swivy/react @swivy/web

2. Configure

Your browser app needs three values from your Swivy environment. They're safe to ship to the browser; the secret key never is.

NEXT_PUBLIC_SWIVY_BASE_URL=https://YOUR_SWIVY_BASE_URL
NEXT_PUBLIC_SWIVY_APP_ID=app_...
NEXT_PUBLIC_SWIVY_PUBLISHABLE_KEY=pk_swivy_...

3. Mount the provider

import { SwivyProvider } from '@swivy/react';

export function AppProviders({ children }: { children: React.ReactNode }) {
  return (
    <SwivyProvider
      config={{
        baseUrl: process.env.NEXT_PUBLIC_SWIVY_BASE_URL!,
        appId: process.env.NEXT_PUBLIC_SWIVY_APP_ID!,
        publishableKey: process.env.NEXT_PUBLIC_SWIVY_PUBLISHABLE_KEY!,
        sessionPersistence: 'localStorage', // keeps users signed in across reloads
      }}
    >
      {children}
    </SwivyProvider>
  );
}

4. Sign in with an email code

import { useState } from 'react';
import { useSwivySession } from '@swivy/react';

export function Login() {
  const auth = useSwivySession();
  const [email, setEmail] = useState('');
  const [code, setCode] = useState('');
  const [sent, setSent] = useState(false);

  if (auth.isLoading) return <p>Loading…</p>;
  if (auth.isAuthenticated) return <p>Signed in.</p>;

  return sent ? (
    <>
      <input value={code} onChange={(e) => setCode(e.target.value)} placeholder="6-digit code" />
      <button onClick={() => auth.signIn(email, code)}>Verify</button>
    </>
  ) : (
    <>
      <input value={email} onChange={(e) => setEmail(e.target.value)} placeholder="you@example.com" />
      <button onClick={async () => { await auth.requestEmailCode(email); setSent(true); }}>Send code</button>
    </>
  );
}

On success Swivy creates any missing wallets your environment policy allows (by default one EVM and one Solana wallet) and returns them in the session as managedAccounts.

Other ways in: auth.signInWithOidcToken(idToken) after Sign in with Swop, or auth.requestWalletLogin() + auth.signInWithWallet() for an existing EVM wallet or Phantom.

5. Load the wallet

import { useEmbeddedWallets } from '@swivy/react';

export function WalletAddress() {
  const wallets = useEmbeddedWallets('evm');
  const wallet = wallets.wallets[0];
  return <code>{wallet?.address ?? 'No wallet yet'}</code>;
}

Call wallets.ensureManagedAccount('evm') to create one explicitly. Use 'solana' for Solana.

6. Enroll a passkey

import { useSwivyPasskeys } from '@swivy/react';

export function PasskeySetup() {
  const passkeys = useSwivyPasskeys();
  return <button onClick={() => passkeys.ensurePasskey('Primary passkey')}>Add a passkey</button>;
}

7. Send a transaction the user approves

import { useEmbeddedWallets, useSwivyIntents, useSwivyPasskeys } from '@swivy/react';

export function Send({ passkeyId }: { passkeyId: string }) {
  const wallets = useEmbeddedWallets('evm');
  const passkeys = useSwivyPasskeys();
  const intents = useSwivyIntents();

  async function send() {
    const wallet = wallets.wallets[0] ?? (await wallets.ensureManagedAccount('evm')).account;
    const approval = await passkeys.approveWithPasskey(passkeyId, 'intent_approval');
    const intent = await intents.createTransactionIntentAndWait({
      accountId: wallet.id,
      chain: 'evm',
      challengeId: approval.challenge.challengeId,
      payload: {
        network: 'base-sepolia',
        transaction: { to: '0x1111111111111111111111111111111111111111', value: '0x0', data: '0x' },
      },
    });
    console.log(intent.status, intent.result);
  }

  return <button onClick={send}>Send</button>;
}

Handling passkey errors

Catch Swivy's stable error codes instead of raw browser exceptions:

import { isSwivyWebAuthnError } from '@swivy/web';

try {
  await passkeys.approveWithPasskey(passkeyId, 'intent_approval');
} catch (error) {
  if (isSwivyWebAuthnError(error)) console.log(error.code, error.recoveryHint);
  throw error;
}
CodeUsually means
webauthn_wrong_originThe page's origin isn't allowed for this environment's passkeys.
webauthn_stale_challengeThe challenge expired. Ask for a new one.
webauthn_missing_credentialThis device has no passkey for the user. Enroll one.
webauthn_canceledThe user dismissed the prompt.

On this page