Blog
    Architecture7 min read

    Cross-App Wallet Identity: The Email-First Approach

    Learn how email-first user identification creates unified credit wallets across multiple applications, eliminating fragmented balances.

    By cred.diy Team

    Cross-App Wallet Identity: The Email-First Approach

    One of the biggest challenges in building a credit platform that serves multiple applications is user identity. When App A and App B both use your credit system, how do you ensure the same user has a single, unified wallet?

    The Problem: Fragmented Identity

    Consider this scenario:

    • App A registers a user with internal ID usr_abc123
    • App B registers the same person with ID 7e34bb43-...
    • Both apps call your credit API with their local IDs
    • The credit system creates two separate wallets for the same human

    This leads to fragmented balances, double-spending confusion, and poor user experience.

    The Solution: Email as Universal Identifier

    Email addresses are the one identifier that remains consistent across applications. By adopting an email-first lookup pattern, you can ensure that:

    1. The same person always maps to the same credit wallet
    2. Apps don't need to coordinate user IDs
    3. Existing integrations continue working via fallback

    How It Works

    When any endpoint receives a user identifier, cred.diy follows this priority chain:

    1. user_email parameter → lookup by email (highest priority)
    2. user_id containing "@" → treated as email lookup
    3. user_id without "@" → lookup by external_id (legacy)
    

    This means existing apps that pass user_id continue working, while new integrations can immediately benefit from unified wallets by passing user_email.

    Implementation Guide

    Issuing Credits

    Always include the user's email when issuing credits:

    const issueCredits = async (userEmail, amount) => {
      const response = await fetch(
        'https://eutdgemlrpvnxfkkpvlu.supabase.co/functions/v1/credits-api/issue',
        {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'x-api-key': process.env.CRED_API_KEY
          },
          body: JSON.stringify({
            user_email: userEmail,     // Recommended
            user_id: 'local-id-123',   // Still supported as fallback
            credit_type_id: 'your_type',
            amount: amount,
            reason: 'Purchase'
          })
        }
      );
      return response.json();
    };
    

    Checking Balances

    // Email-first balance check
    const getBalance = async (email) => {
      const response = await fetch(
        `https://eutdgemlrpvnxfkkpvlu.supabase.co/functions/v1/credits-api/balance?email=${encodeURIComponent(email)}`,
        {
          headers: { 'x-api-key': process.env.CRED_API_KEY }
        }
      );
      return response.json();
    };
    

    Deducting Credits

    const deductCredits = async (userEmail, amount, reason) => {
      const response = await fetch(
        'https://eutdgemlrpvnxfkkpvlu.supabase.co/functions/v1/credits-api/deduct',
        {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'x-api-key': process.env.CRED_API_KEY
          },
          body: JSON.stringify({
            user_email: userEmail,
            credit_type_id: 'your_type',
            amount: amount,
            reason: reason
          })
        }
      );
      return response.json();
    };
    

    Migration Strategy

    If you're transitioning from ID-based to email-based lookups:

    Step 1: Start Passing Emails

    Update your API calls to include user_email alongside user_id. The system will prioritize email but fall back to ID.

    Step 2: Update User Records

    Ensure all your end users have email addresses stored:

    // Update existing users with their emails
    const updateUser = async (userId, email) => {
      await fetch('https://eutdgemlrpvnxfkkpvlu.supabase.co/functions/v1/credits-api/users', {
        method: 'PATCH',
        headers: {
          'Content-Type': 'application/json',
          'x-api-key': process.env.CRED_API_KEY
        },
        body: JSON.stringify({
          external_id: userId,
          email: email
        })
      });
    };
    

    Step 3: Remove ID Dependencies

    Once all users have emails, you can simplify your integration to use email exclusively.

    Benefits

    AspectID-BasedEmail-First
    Cross-app identity❌ Fragmented✅ Unified
    Migration effortN/ALow (additive)
    Backwards compatibleN/A✅ Yes
    User experienceConfusingSeamless
    Balance portability❌ Per-app✅ Universal

    Best Practices

    1. Always store user emails when creating end users
    2. Use email as the primary identifier in new integrations
    3. Keep user_id as a fallback for legacy support
    4. Validate email format before sending to the API
    5. Handle edge cases like email changes with user merging

    Conclusion

    The email-first approach transforms your credit system from a per-app utility into a true cross-platform wallet. Users get a seamless experience, developers get simpler integrations, and your platform becomes the unified layer connecting it all.

    Start building with email-first identity today.

    Try it on a real project.