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

# Customer Account

> Customer registration, profile management, and social features

## Overview

The Customer Account API handles customer registration, profile management, and social features including following other customers and stores. This system integrates with Clerk authentication to provide comprehensive user account management.

**Location:** `convex/customers/account.ts`

## Register Customer

Register a new customer or update existing customer preferences. New customers automatically get:

* Auto-generated unique username
* Default private account (followers only)
* Profile ready for avatar upload

<ParamField path="preferredLanguage" type="'en' | 'ar'">
  Preferred language for the customer (default: "en")
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const customerId = await convex.mutation(api.customers.account.registerCustomer, {
    preferredLanguage: "en"
  });
  ```

  ```javascript JavaScript theme={null}
  const customerId = await convex.mutation("customers.account.registerCustomer", {
    preferredLanguage: "ar"
  });
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      f"{CONVEX_URL}/api/mutation",
      json={
          "path": "customers.account.registerCustomer",
          "args": {
              "preferredLanguage": "en"
          }
      },
      headers={"Authorization": f"Bearer {token}"}
  )

  customer_id = response.json()
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  "c123456789"
  ```
</ResponseExample>

## Update Customer Profile

Update the authenticated customer's profile details.

<ParamField path="name" type="string">
  Display name of the customer
</ParamField>

<ParamField path="email" type="string">
  Email address of the customer
</ParamField>

<ParamField path="phoneNumber" type="string">
  Phone number of the customer
</ParamField>

<ParamField path="isPrivate" type="boolean">
  Account privacy setting (true = private, false = public)
</ParamField>

<ParamField path="preferences.preferredLanguage" type="'en' | 'ar'">
  Preferred language
</ParamField>

<ParamField path="preferences.pushNotifications" type="boolean">
  Enable push notifications
</ParamField>

<ParamField path="preferences.emailNotifications" type="boolean">
  Enable email notifications
</ParamField>

<ParamField path="preferences.smsNotifications" type="boolean">
  Enable SMS notifications
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const updatedCustomerId = await convex.mutation(api.customers.account.updateCustomer, {
    name: "John Doe",
    email: "john@example.com",
    phoneNumber: "+971501234567",
    preferences: {
      preferredLanguage: "en",
      pushNotifications: true,
      emailNotifications: true,
      smsNotifications: false,
    },
  });
  ```

  ```javascript JavaScript theme={null}
  const updatedCustomerId = await convex.mutation("customers.account.updateCustomer", {
    name: "John Doe",
    email: "john@example.com",
    phoneNumber: "+971501234567",
    preferences: {
      preferredLanguage: "ar",
      pushNotifications: true,
      emailNotifications: false,
      smsNotifications: true,
    },
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  "c123456789"
  ```
</ResponseExample>

## Avatar Management

### Update Avatar

Upload and set a new avatar image for the customer.

<ParamField path="avatarId" type="Id<'_storage'>" required>
  Storage ID of the uploaded avatar image
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const customerId = await convex.mutation(api.customers.account.updateAvatar, {
    avatarId: "storage_abc123"
  });
  ```
</CodeGroup>

### Remove Avatar

Remove the customer's current avatar image.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const customerId = await convex.mutation(api.customers.account.removeAvatar);
  ```
</CodeGroup>

## Username Management

### Update Username

Change the customer's username (must be unique).

<ParamField path="username" type="string" required>
  New username (3-20 alphanumeric characters)
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const customerId = await convex.mutation(api.customers.account.updateUsername, {
    username: "newusername"
  });
  ```
</CodeGroup>

### Check Username Availability

Check if a username is available and get suggestions.

<ParamField path="username" type="string" required>
  Username to check
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const availability = await convex.query(api.customers.account.checkUsernameAvailability, {
    username: "desiredusername"
  });

  // Response: { available: false, suggestion: "desiredusername1" }
  ```
</CodeGroup>

### Search Customers by Username

Search for customers by their username.

<ParamField path="searchTerm" type="string" required>
  Search term to match usernames
</ParamField>

<ParamField path="limit" type="number">
  Maximum number of results (default: 20)
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const customers = await convex.query(api.customers.account.searchCustomersByUsername, {
    searchTerm: "john",
    limit: 10
  });
  ```
</CodeGroup>

## Privacy Controls

### Update Privacy Setting

Change account privacy (private accounts show content only to followers).

<ParamField path="isPrivate" type="boolean" required>
  Privacy setting (true = private, false = public)
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const customerId = await convex.mutation(api.customers.account.updatePrivacy, {
    isPrivate: false // Make account public
  });
  ```
</CodeGroup>

## Get Customer Profile

Retrieve customer profile information for the authenticated user or a specific customer. Respects privacy settings - private accounts are only visible to followers.

<ParamField path="customerId" type="Id<'customers'>">
  Customer ID (defaults to authenticated user)
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  // Get own profile
  const myProfile = await convex.query(api.customers.account.getCustomerProfile);

  // Get specific customer profile
  const customerProfile = await convex.query(api.customers.account.getCustomerProfile, {
    customerId: "c123456789"
  });
  ```

  ```javascript JavaScript theme={null}
  const profile = await convex.query("customers.account.getCustomerProfile");
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "_id": "c123456789",
    "userId": "user_2abc123def456",
    "name": "John Doe",
    "username": "johndoe",
    "avatarId": "storage_abc123",
    "avatarUrl": "https://storage.convex.dev/profile.jpg",
    "email": "john@example.com",
    "phoneNumber": "+971501234567",
    "isPrivate": true,
    "stats": {
      "totalOrders": 25,
      "totalSpent": 1250.75,
      "averageOrderValue": 50.03,
      "favoriteStores": 8,
      "reviewsWritten": 12
    },
    "followersCount": 45,
    "followingCustomersCount": 32,
    "followingStoresCount": 15,
    "preferences": {
      "notifications": {
        "orderUpdates": true,
        "promotions": true,
        "newStores": false
      },
      "privacy": {
        "showProfile": true,
        "showOrders": false,
        "showFollowers": true
      }
    },
    "_creationTime": 1640995200000,
    "lastActive": 1640995800000
  }
  ```
</ResponseExample>

## Get Customer Followings

Retrieve stores and customers that the authenticated user is following. Returns a plain object with `stores` and `customers` arrays (not paginated).

<ParamField path="customerId" type="Id<'customers'>">
  Customer ID (defaults to authenticated user)
</ParamField>

<ParamField path="type" type="'stores' | 'customers' | 'all'">
  Filter by follow type (default: "all")
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const followings = await convex.query(api.customers.account.getCustomerFollowings, {
    type: "all"
  });
  ```

  ```javascript JavaScript theme={null}
  const followings = await convex.query("customers.account.getCustomerFollowings", {
    customerId: "c123456789",
    type: "stores"
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "stores": [
      {
        "_id": "j123456789",
        "name": "Mario's Pizza Palace",
        "logoId": "storage_abc123",
        "logoUrl": "https://storage.convex.dev/mario-logo.jpg",
        "primaryCategory": "k123456789",
        "followersCount": 120,
        "isActive": true
      }
    ],
    "customers": [
      {
        "_id": "c987654321",
        "name": "Jane Smith",
        "username": "janesmith",
        "avatarId": "storage_def456",
        "avatarUrl": "https://storage.convex.dev/jane.jpg",
        "followersCount": 45
      }
    ]
  }
  ```
</ResponseExample>

## Get Customer Followers

Retrieve customers that follow the authenticated user. Returns a plain array of follower objects (not paginated).

<ParamField path="customerId" type="Id<'customers'>">
  Customer ID (defaults to authenticated user)
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const followers = await convex.query(api.customers.account.getCustomerFollowers);
  ```

  ```javascript JavaScript theme={null}
  const followers = await convex.query("customers.account.getCustomerFollowers", {
    customerId: "c123456789"
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  [
    {
      "_id": "c111222333",
      "name": "Sarah Wilson",
      "username": "sarahw",
      "avatarId": "storage_ghi789",
      "avatarUrl": "https://storage.convex.dev/sarah.jpg",
      "followersCount": 30
    },
    {
      "_id": "c444555666",
      "name": "Mike Johnson",
      "username": "mikej",
      "avatarId": null,
      "avatarUrl": null,
      "followersCount": 12
    }
  ]
  ```
</ResponseExample>

## Customer Profile Component

Complete customer profile management:

<CodeGroup>
  ```typescript React Profile Component theme={null}
  import { useQuery, useMutation } from 'convex/react';
  import { useUser } from '@clerk/nextjs';
  import { api } from './convex/_generated/api';

  function CustomerProfile() {
    const { user } = useUser();
    const profile = useQuery(api.customers.account.getCustomerProfile);
    const followings = useQuery(api.customers.account.getCustomerFollowings, {
      type: "all"
    });
    const followers = useQuery(api.customers.account.getCustomerFollowers);

    if (!profile) {
      return <div>Loading profile...</div>;
    }

    return (
      <div className="max-w-4xl mx-auto space-y-6">
        {/* Profile Header */}
        <div className="bg-white rounded-lg shadow p-6">
          <div className="flex items-center space-x-6">
            <div className="w-24 h-24 rounded-full overflow-hidden bg-gray-200">
              {profile.profileImage ? (
                <img 
                  src={profile.profileImage} 
                  alt={profile.name}
                  className="w-full h-full object-cover"
                />
              ) : (
                <div className="w-full h-full flex items-center justify-center text-gray-500 text-2xl">
                  {profile.name?.charAt(0) || '?'}
                </div>
              )}
            </div>
            
            <div className="flex-1">
              <h1 className="text-2xl font-bold text-gray-900">{profile.name}</h1>
              <p className="text-gray-600">{profile.email}</p>
              <p className="text-gray-600">{profile.phoneNumber}</p>
              
              <div className="flex items-center space-x-4 mt-2">
                <span className="text-sm text-gray-500">
                  Member since {new Date(profile._creationTime).toLocaleDateString()}
                </span>
                <span className={`px-2 py-1 rounded-full text-xs font-medium ${
                  profile.preferredLanguage === 'en' 
                    ? 'bg-blue-100 text-blue-800' 
                    : 'bg-green-100 text-green-800'
                }`}>
                  {profile.preferredLanguage === 'en' ? 'English' : 'العربية'}
                </span>
              </div>
            </div>
          </div>
        </div>

        {/* Stats Grid */}
        <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
          <div className="bg-white p-4 rounded-lg shadow text-center">
            <p className="text-2xl font-bold text-blue-600">{profile.stats.totalOrders}</p>
            <p className="text-sm text-gray-600">Total Orders</p>
          </div>
          
          <div className="bg-white p-4 rounded-lg shadow text-center">
            <p className="text-2xl font-bold text-green-600">${profile.stats.totalSpent.toFixed(2)}</p>
            <p className="text-sm text-gray-600">Total Spent</p>
          </div>
          
          <div className="bg-white p-4 rounded-lg shadow text-center">
            <p className="text-2xl font-bold text-purple-600">{profile.stats.reviewsWritten}</p>
            <p className="text-sm text-gray-600">Reviews Written</p>
          </div>
          
          <div className="bg-white p-4 rounded-lg shadow text-center">
            <p className="text-2xl font-bold text-orange-600">{profile.stats.favoriteStores}</p>
            <p className="text-sm text-gray-600">Favorite Stores</p>
          </div>
        </div>

        {/* Social Stats */}
        <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
          <div className="bg-white p-4 rounded-lg shadow text-center">
            <p className="text-xl font-bold text-gray-900">{profile.socialStats.followersCount}</p>
            <p className="text-sm text-gray-600">Followers</p>
          </div>
          
          <div className="bg-white p-4 rounded-lg shadow text-center">
            <p className="text-xl font-bold text-gray-900">{profile.socialStats.followingCount}</p>
            <p className="text-sm text-gray-600">Following</p>
          </div>
          
          <div className="bg-white p-4 rounded-lg shadow text-center">
            <p className="text-xl font-bold text-gray-900">{profile.socialStats.storesFollowingCount}</p>
            <p className="text-sm text-gray-600">Stores Following</p>
          </div>
        </div>

        {/* Following List - Customers */}
        {followings && followings.customers.length > 0 && (
          <div className="bg-white rounded-lg shadow p-6">
            <h2 className="text-lg font-semibold mb-4">Following Customers</h2>
            <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
              {followings.customers.map((customer) => (
                <div key={customer._id} className="flex items-center space-x-3 p-3 bg-gray-50 rounded">
                  <div className="w-10 h-10 rounded-full overflow-hidden bg-gray-200">
                    {customer.avatarUrl ? (
                      <img
                        src={customer.avatarUrl}
                        alt={customer.name}
                        className="w-full h-full object-cover"
                      />
                    ) : (
                      <div className="w-full h-full flex items-center justify-center text-gray-500 text-sm">
                        {customer.name?.charAt(0) || '?'}
                      </div>
                    )}
                  </div>
                  <div className="flex-1 min-w-0">
                    <p className="font-medium text-gray-900 truncate">{customer.name}</p>
                    <p className="text-sm text-gray-600">@{customer.username}</p>
                  </div>
                </div>
              ))}
            </div>
          </div>
        )}

        {/* Followers List */}
        {followers && followers.length > 0 && (
          <div className="bg-white rounded-lg shadow p-6">
            <h2 className="text-lg font-semibold mb-4">Followers ({profile.followersCount})</h2>
            <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
              {followers.map((customer) => (
                <div key={customer._id} className="flex items-center space-x-3 p-3 bg-gray-50 rounded">
                  <div className="w-10 h-10 rounded-full overflow-hidden bg-gray-200">
                    {customer.avatarUrl ? (
                      <img
                        src={customer.avatarUrl}
                        alt={customer.name}
                        className="w-full h-full object-cover"
                      />
                    ) : (
                      <div className="w-full h-full flex items-center justify-center text-gray-500 text-sm">
                        {customer.name?.charAt(0) || '?'}
                      </div>
                    )}
                  </div>
                  <div className="flex-1 min-w-0">
                    <p className="font-medium text-gray-900 truncate">{customer.name}</p>
                    <p className="text-sm text-gray-600">@{customer.username}</p>
                  </div>
                </div>
              ))}
            </div>
          </div>
        )}
      </div>
    );
  }

  export default CustomerProfile;
  ```

  ```typescript Customer Registration Hook theme={null}
  import { useEffect } from 'react';
  import { useUser } from '@clerk/nextjs';
  import { useQuery, useMutation } from 'convex/react';
  import { api } from './convex/_generated/api';

  function useCustomerRegistration() {
    const { user, isLoaded } = useUser();
    const profile = useQuery(api.customers.account.getCustomerProfile);
    const registerCustomer = useMutation(api.customers.account.registerCustomer);

    // Auto-register customer when user signs in for the first time
    useEffect(() => {
      if (isLoaded && user && profile === null) {
        // User is authenticated but no customer profile exists
        const autoRegister = async () => {
          try {
            const customerId = await registerCustomer({
              preferredLanguage: "en" // Default to English, can be changed later
            });
            
            console.log('Customer auto-registered:', customerId);
            
            // Update Clerk user metadata
            await user.update({
              publicMetadata: {
                ...user.publicMetadata,
                role: 'customer',
                customerId: customerId
              }
            });
            
          } catch (error) {
            console.error('Auto-registration failed:', error);
          }
        };

        autoRegister();
      }
    }, [isLoaded, user, profile, registerCustomer]);

    return {
      profile,
      isLoading: profile === undefined,
      isRegistered: profile !== null,
      needsRegistration: isLoaded && user && profile === null
    };
  }

  export { useCustomerRegistration };
  ```
</CodeGroup>

## Customer Onboarding Flow

Complete onboarding process for new customers:

<CodeGroup>
  ```typescript React Onboarding theme={null}
  import { useState } from 'react';
  import { useUser } from '@clerk/nextjs';
  import { useMutation } from 'convex/react';
  import { api } from './convex/_generated/api';

  interface OnboardingProps {
    onComplete: () => void;
  }

  function CustomerOnboarding({ onComplete }: OnboardingProps) {
    const { user } = useUser();
    const [step, setStep] = useState(1);
    const [preferences, setPreferences] = useState({
      preferredLanguage: 'en' as 'en' | 'ar',
      notifications: {
        orderUpdates: true,
        promotions: true,
        newStores: false
      }
    });
    const [loading, setLoading] = useState(false);

    const registerCustomer = useMutation(api.customers.account.registerCustomer);

    const handleComplete = async () => {
      if (!user) return;

      setLoading(true);
      try {
        // Register customer
        const customerId = await registerCustomer({
          preferredLanguage: preferences.preferredLanguage
        });

        // Update Clerk metadata
        await user.update({
          publicMetadata: {
            role: 'customer',
            customerId: customerId,
            onboardingCompleted: true
          }
        });

        onComplete();
      } catch (error) {
        console.error('Onboarding failed:', error);
      } finally {
        setLoading(false);
      }
    };

    return (
      <div className="max-w-md mx-auto bg-white p-6 rounded-lg shadow">
        <div className="text-center mb-6">
          <h2 className="text-2xl font-bold text-gray-900">Welcome to Twigz! 🎉</h2>
          <p className="text-gray-600 mt-2">Let's set up your account</p>
        </div>

        {/* Progress Indicator */}
        <div className="flex justify-center mb-6">
          <div className="flex space-x-2">
            {[1, 2, 3].map((stepNum) => (
              <div
                key={stepNum}
                className={`w-3 h-3 rounded-full ${
                  step >= stepNum ? 'bg-blue-600' : 'bg-gray-300'
                }`}
              />
            ))}
          </div>
        </div>

        {/* Step Content */}
        {step === 1 && (
          <div className="space-y-4">
            <h3 className="font-semibold">Choose Your Language</h3>
            <div className="space-y-2">
              <label className="flex items-center space-x-3">
                <input
                  type="radio"
                  name="language"
                  value="en"
                  checked={preferences.preferredLanguage === 'en'}
                  onChange={(e) => setPreferences(prev => ({ 
                    ...prev, 
                    preferredLanguage: e.target.value as 'en' 
                  }))}
                  className="text-blue-600"
                />
                <span>English</span>
              </label>
              <label className="flex items-center space-x-3">
                <input
                  type="radio"
                  name="language"
                  value="ar"
                  checked={preferences.preferredLanguage === 'ar'}
                  onChange={(e) => setPreferences(prev => ({ 
                    ...prev, 
                    preferredLanguage: e.target.value as 'ar' 
                  }))}
                  className="text-blue-600"
                />
                <span>العربية (Arabic)</span>
              </label>
            </div>
          </div>
        )}

        {step === 2 && (
          <div className="space-y-4">
            <h3 className="font-semibold">Notification Preferences</h3>
            <div className="space-y-3">
              <label className="flex items-center justify-between">
                <span>Order Updates</span>
                <input
                  type="checkbox"
                  checked={preferences.notifications.orderUpdates}
                  onChange={(e) => setPreferences(prev => ({
                    ...prev,
                    notifications: {
                      ...prev.notifications,
                      orderUpdates: e.target.checked
                    }
                  }))}
                  className="text-blue-600"
                />
              </label>
              
              <label className="flex items-center justify-between">
                <span>Promotions & Offers</span>
                <input
                  type="checkbox"
                  checked={preferences.notifications.promotions}
                  onChange={(e) => setPreferences(prev => ({
                    ...prev,
                    notifications: {
                      ...prev.notifications,
                      promotions: e.target.checked
                    }
                  }))}
                  className="text-blue-600"
                />
              </label>
              
              <label className="flex items-center justify-between">
                <span>New Store Alerts</span>
                <input
                  type="checkbox"
                  checked={preferences.notifications.newStores}
                  onChange={(e) => setPreferences(prev => ({
                    ...prev,
                    notifications: {
                      ...prev.notifications,
                      newStores: e.target.checked
                    }
                  }))}
                  className="text-blue-600"
                />
              </label>
            </div>
          </div>
        )}

        {step === 3 && (
          <div className="text-center space-y-4">
            <div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto">
              <svg className="w-8 h-8 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
              </svg>
            </div>
            <h3 className="font-semibold">You're All Set!</h3>
            <p className="text-gray-600">
              Your account is ready. Start exploring amazing stores and products!
            </p>
          </div>
        )}

        {/* Navigation Buttons */}
        <div className="flex justify-between mt-6">
          {step > 1 && step < 3 && (
            <button
              onClick={() => setStep(step - 1)}
              className="px-4 py-2 text-gray-600 hover:text-gray-800"
            >
              Back
            </button>
          )}
          
          <div className="ml-auto">
            {step < 3 ? (
              <button
                onClick={() => setStep(step + 1)}
                className="px-6 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
              >
                Next
              </button>
            ) : (
              <button
                onClick={handleComplete}
                disabled={loading}
                className="px-6 py-2 bg-green-600 text-white rounded hover:bg-green-700 disabled:opacity-50"
              >
                {loading ? 'Setting up...' : 'Get Started'}
              </button>
            )}
          </div>
        </div>
      </div>
    );
  }

  export default CustomerOnboarding;
  ```
</CodeGroup>

## Social Features

The account system includes social networking features:

<CardGroup cols={2}>
  <Card title="Follow Customers" icon="user-plus">
    Follow other customers to see their activity in your feed and discover new products through their interactions.
  </Card>

  <Card title="Follow Stores" icon="store">
    Follow favorite stores to get notifications about new products, promotions, and special offers.
  </Card>

  <Card title="Activity Feed" icon="rss">
    See activity from followed customers and stores including new products, reviews, and recommendations.
  </Card>

  <Card title="Social Discovery" icon="compass">
    Discover new stores and products through your social network and trending content.
  </Card>
</CardGroup>

## Privacy Settings

Customers can control their privacy preferences:

```typescript theme={null}
interface CustomerPrivacySettings {
  showProfile: boolean;        // Profile visible to other users
  showOrders: boolean;         // Order history visible to followers
  showFollowers: boolean;      // Follower list visible to others
  showActivity: boolean;       // Activity visible in feeds
  allowMessages: boolean;      // Allow direct messages
  showLocation: boolean;       // Show general location (city/area)
}
```

## Error Handling

<AccordionGroup>
  <Accordion title="Customer already registered">
    **Status Code**: `200` (Success, returns existing customer)

    ```json theme={null}
    {
      "customerId": "c123456789",
      "message": "Customer already exists, preferences updated"
    }
    ```
  </Accordion>

  <Accordion title="Invalid language preference">
    **Status Code**: `400`

    ```json theme={null}
    {
      "error": "Invalid language preference",
      "supportedLanguages": ["en", "ar"]
    }
    ```
  </Accordion>

  <Accordion title="Profile not found">
    **Status Code**: `404`

    ```json theme={null}
    {
      "error": "Customer profile not found",
      "customerId": "c123456789"
    }
    ```
  </Accordion>
</AccordionGroup>

<Note>
  Customer registration is idempotent - calling it multiple times for the same user will update preferences rather than create duplicate accounts.
</Note>

<Tip>
  Use the auto-registration pattern to seamlessly onboard users when they first authenticate, then guide them through preference setup.
</Tip>
