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

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:
- The same person always maps to the same credit wallet
- Apps don't need to coordinate user IDs
- 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
| Aspect | ID-Based | Email-First |
|---|---|---|
| Cross-app identity | ❌ Fragmented | ✅ Unified |
| Migration effort | N/A | Low (additive) |
| Backwards compatible | N/A | ✅ Yes |
| User experience | Confusing | Seamless |
| Balance portability | ❌ Per-app | ✅ Universal |
Best Practices
- Always store user emails when creating end users
- Use email as the primary identifier in new integrations
- Keep user_id as a fallback for legacy support
- Validate email format before sending to the API
- 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.

AI Agents Can Pay for API Access. Your Payment Boundary Still Needs Work.
A practical October 2026 checklist for x402 payments: verify challenges, constrain agent spending, separate payment from execution, and respect testnet limits.

Paid, but No Credits? A Q4 2026 Checklist for Reliable Wallet Funding
Why payment success and credit delivery are separate states—and how paid-invoice references, reconciliation, signed webhooks, and atomic updates keep them aligned.