# Signup flow

Render `<YodlSignup />` when a user needs a Yodl account. The hosted UI owns the whole flow: the status check, SIWE, email capture, verification code entry, and the final signed-up screen. You render one component and wait for the callback.

```tsx [SignupScreen.tsx]
import { useState } from 'react';
import { Text } from 'react-native';
import { YodlProvider, YodlSignup } from '@yodlpay/react-native';
import { sdk } from './sdk';
import { useWallet } from './wallet';

export function SignupScreen() {
  const { provider, address, chainId, isConnected } = useWallet();
  const [showSignup, setShowSignup] = useState(true);

  if (!isConnected) return <Text>Connect a wallet first.</Text>;
  if (!showSignup) return null;

  return (
    <YodlProvider sdk={sdk} provider={provider} address={address} chainId={chainId}>
      <YodlSignup
        onStatusChange={(status) => console.log('signup status:', status)}
        onSignedUp={() => {
          // Store completion however your app tracks user state.
          setShowSignup(false);
        }}
        onDismiss={() => setShowSignup(false)}
        onError={console.error}
      />
    </YodlProvider>
  );
}
```

`<YodlSignup />` is a full-screen component and must be rendered inside `<YodlProvider>`.

## While it loads

The hosted flow is a WebView, so there is a beat before it paints, and the SDK covers it with an overlay in the meantime. `<YodlSignup />` takes `backgroundColor`, `renderLoading` and `onReady` on exactly the same terms as the other two components — including the choice between putting your loader inside Yodl's overlay and suppressing it in favour of your own screen. See [loading and readiness](/sdk/react-native/api#loading-and-readiness).

## Tracking completion yourself

:::warning
**The SDK exposes no account details from the hosted flow.** `onSignedUp` tells you the user finished — nothing more. If you need to remember that, persist it in your own app state, storage or backend. There is no API to query it back from the SDK.
:::

## Status values

`onStatusChange` reports the hosted flow's progress:

| Status | Meaning |
| --- | --- |
| `checking` | Determining whether the wallet already has an account |
| `signed_out` | No account — the flow will start signup |
| `signing_up` | The user is partway through |
| `signed_up` | Complete |

`onDismiss` fires when the user closes the success screen, which is separate from `onSignedUp` — a user can complete signup and leave the screen up.

## Skipping email verification

If your app has already verified the user's email address, you can assert that with a short-lived JWT your backend signs, and the user skips Yodl's own OTP step.

Pass `fetchEmailAttestation`. The hosted flow calls it at the moment it creates the account — after the wallet signature, once it knows which wallet to bind:

```tsx [SignupScreen.tsx]
<YodlSignup
  fetchEmailAttestation={async ({ address, signal }) => {
    // Your backend authenticates the user itself and signs for the email it
    // has already verified. Never sign on the device.
    const response = await fetch('https://api.yourwallet.com/yodl/email-attestation', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${session}` },
      body: JSON.stringify({ walletAddress: address }),
      signal,
    });
    if (!response.ok) return null;

    const { token, email } = await response.json();
    return { token, email };
  }}
  onSignedUp={() => setShowSignup(false)}
/>
```

The request carries the wallet and nothing else — there is no email in it to echo back. The `email` you return is the one the Yodl account is created with, so return the address your backend actually verified. A token with no `email` is treated as a decline.

Return `null` and the user verifies by code as usual — a normal outcome, not an error, and it never reaches `onError`. The same is true of a throw or a timeout. Omitting the prop entirely leaves the flow exactly as it is today.

`<YodlPayment />` and `<YodlDashboard />` take the same prop on the same terms. A wallet with no Yodl account is asked to register inside those flows too, so an app with no separate signup screen still gets the OTP skip — see [the API reference](/sdk/react-native/api#fetchemailattestation).

:::warning
**Mint on demand, never cache.** The token is single use and valid for at most 5 minutes. Yodl calls your function fresh for every attempt precisely so each one gets an unspent token — returning a stored one fails, and burns it for good.
:::

The claim set your backend must produce, the JWKS URL you register, and the verification order are in [email attestation](/integrations/email-attestation). It is granted per integration and off by default, so **until we enable it for you a valid token changes nothing and the user still gets the OTP**.

Full prop tables are in the [API reference](/sdk/react-native/api#yodlsignup).
