Migration Guide
Guide for upgrading Loyalteez integrations and handling API changes.
Current Version
SDK Version: 2.0
API Version: v2
This is the current production version with full support and all features documented in this guide.
Upgrading to v2.0
What's New
✨ New Features:
- OAuth wallet pregeneration (8 providers)
- Idempotent API endpoints
- Enhanced error messages
created_newflag in responses- Gas Relayer EIP-2612 permit support
- Real-time reward toasts in SDK
- Client-side event deduplication (2s window)
🔧 Improvements:
- Faster event tracking (< 50ms)
- Better rate limit handling
- Comprehensive error codes
- TypeScript definitions
- Enhanced security
🐛 Bug Fixes:
- Fixed duplicate event tracking
- Resolved wallet creation race conditions
- Fixed Privy token expiration handling
Breaking Changes
1. SDK Initialization
❌ Old (v1.x):
LoyalteezAutomation.init({
brand: 'your-brand-id',
apiEndpoint: 'https://api.loyalteez.app'
});
✅ New (v2.0):
LoyalteezAutomation.init('your-brand-id', {
endpoint: 'https://api.loyalteez.app', // Changed key
debug: false,
autoDetect: false
});
Migration:
// Find in your code:
LoyalteezAutomation.init({
brand: brandId,
apiEndpoint: endpoint
});
// Replace with:
LoyalteezAutomation.init(brandId, {
endpoint: endpoint
});
2. Event Response Format
❌ Old Response (v1.x):
{
"success": true,
"event_id": "123",
"reward": 100
}
✅ New Response (v2.0):
{
"success": true,
"eventId": "123",
"rewardAmount": 100,
"walletCreated": true,
"walletAddress": "0x..."
}
Migration:
// ❌ Old
const { event_id, reward } = response;
// ✅ New
const { eventId, rewardAmount, walletAddress } = response;
3. Error Response Format
❌ Old (v1.x):
{
"error": true,
"message": "Failed"
}
✅ New (v2.0):
{
"error": "Rate limit exceeded",
"message": "Too many events from this email. Maximum 1 reward per event type per day per user.",
"details": ["Additional context"]
}
Migration:
// ❌ Old
if (response.error) {
console.log(response.message);
}
// ✅ New
if (response.error) {
console.log(`${response.error}: ${response.message}`);
if (response.details) {
console.log('Details:', response.details);
}
}
4. Gas Relayer Authentication
❌ Old (v1.x):
// Used API key
headers: {
'X-API-Key': 'your-api-key'
}
✅ New (v2.0):
// Uses Privy token
const token = await privy.getAccessToken();
headers: {
'Authorization': `Bearer ${token}`
}
Migration:
// ❌ Old
async function executeTransaction(txData) {
const response = await fetch('https://relayer.loyalteez.app/relay', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': process.env.API_KEY
},
body: JSON.stringify(txData)
});
}
// ✅ New
async function executeTransaction(txData) {
const token = await privy.getAccessToken();
const response = await fetch('https://relayer.loyalteez.app/relay', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(txData)
});
}
Non-Breaking Changes
These changes are backward compatible but recommended:
1. Event Name Mapping
New Feature (v2.0):
LoyalteezAutomation.init('your-brand-id', {
eventNameMapping: {
'newsletter_signup': 'newsletter_subscribe',
'user_registered': 'account_creation'
}
});
Migration (Optional):
// Instead of manually mapping in your code:
let eventType = detectedEventType;
if (eventType === 'newsletter_signup') {
eventType = 'newsletter_subscribe';
}
LoyalteezAutomation.track(eventType, data);
// Use built-in mapping:
LoyalteezAutomation.init('your-brand-id', {
eventNameMapping: {
'newsletter_signup': 'newsletter_subscribe'
}
});
LoyalteezAutomation.track('newsletter_signup', data); // Auto-mapped
2. OAuth Pregeneration
New Feature (v2.0):
Create wallets before user login:
// New endpoint
const response = await fetch(
'https://register.loyalteez.app/loyalteez-api/pregenerate-user',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
brand_id: 'your-brand-id',
oauth_provider: 'discord',
oauth_user_id: discordUserId,
oauth_username: discordUsername
})
}
);
const { wallet_address, created_new } = await response.json();
No migration needed - This is a new feature.
3. Reward Toast Notifications
New Feature (v2.0):
SDK now automatically shows toast notifications when rewards are earned.
Customize:
// Override default toast
LoyalteezAutomation.showRewardToast = function(amount, eventType, userEmail) {
// Your custom notification logic
alert(`You earned ${amount} LTZ!`);
};
Migration Steps
Step 1: Update SDK
If using CDN:
<!-- ❌ Old -->
<script src="https://api.loyalteez.app/sdk.js?v=1"></script>
<!-- ✅ New -->
<script src="https://api.loyalteez.app/sdk.js"></script>
If using npm (future):
npm update @loyalteez/sdk
Step 2: Update Initialization
// ❌ Old
LoyalteezAutomation.init({
brand: 'your-brand-id',
apiEndpoint: 'https://api.loyalteez.app',
debug: true
});
// ✅ New
LoyalteezAutomation.init('your-brand-id', {
endpoint: 'https://api.loyalteez.app',
debug: true
});
Step 3: Update Response Handling
// ❌ Old
async function trackEvent(eventType, userEmail) {
const response = await fetch(...);
const { event_id, reward } = await response.json();
console.log(`Event ${event_id}: ${reward} LTZ`);
}
// ✅ New
async function trackEvent(eventType, userEmail) {
const response = await fetch(...);
const { eventId, rewardAmount, walletAddress } = await response.json();
console.log(`Event ${eventId}: ${rewardAmount} LTZ to ${walletAddress}`);
}
Step 4: Update Error Handling
// ❌ Old
try {
await trackEvent(...);
} catch (error) {
console.error(error.message);
}
// ✅ New
try {
await trackEvent(...);
} catch (error) {
console.error(`${error.error}: ${error.message}`);
// Handle specific errors
if (error.status === 429) {
// Rate limited
} else if (error.status === 403) {
// Automation disabled
}
}
Step 5: Update Gas Relayer Calls
// ❌ Old
async function relayTransaction(txData) {
await fetch('https://relayer.loyalteez.app/relay', {
headers: {
'X-API-Key': apiKey
},
body: JSON.stringify(txData)
});
}
// ✅ New
async function relayTransaction(txData) {
const token = await privy.getAccessToken();
await fetch('https://relayer.loyalteez.app/relay', {
headers: {
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(txData)
});
}
Step 6: Test Thoroughly
Run through these tests:
- ✅ Event tracking works
- ✅ Error handling works
- ✅ Rate limiting works
- ✅ Gas relayer works (if used)
- ✅ Toast notifications appear
- ✅ Wallet creation works
Automated Migration Script
migrate-v1-to-v2.js:
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
function migrateFile(filePath) {
let content = fs.readFileSync(filePath, 'utf8');
// Update SDK initialization
content = content.replace(
/LoyalteezAutomation\.init\(\{[\s\S]*?brand:\s*['"]([^'"]+)['"][\s\S]*?\}\)/g,
(match, brandId) => {
return `LoyalteezAutomation.init('${brandId}', {\n endpoint: 'https://api.loyalteez.app',\n debug: true\n})`;
}
);
// Update response property names
content = content.replace(/\.event_id\b/g, '.eventId');
content = content.replace(/\.reward\b/g, '.rewardAmount');
// Update Gas Relayer auth
content = content.replace(
/'X-API-Key':\s*[^,\}]+/g,
"'Authorization': `Bearer ${token}`"
);
fs.writeFileSync(filePath, content);
console.log(`✅ Migrated: ${filePath}`);
}
// Find and migrate all JS files
function migrateDirectory(dir) {
const files = fs.readdirSync(dir);
for (const file of files) {
const filePath = path.join(dir, file);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
if (!file.startsWith('.') && file !== 'node_modules') {
migrateDirectory(filePath);
}
} else if (file.endsWith('.js') || file.endsWith('.jsx')) {
migrateFile(filePath);
}
}
}
// Run migration
migrateDirectory(process.cwd());
console.log('🎉 Migration complete!');
Usage:
node migrate-v1-to-v2.js
Rollback Plan
If you encounter issues after upgrading:
Quick Rollback (CDN)
<!-- Rollback to v1 -->
<script src="https://api.loyalteez.app/sdk.js?v=1"></script>
Full Rollback (npm)
# Rollback SDK
npm install @loyalteez/[email protected]
# Revert code changes
git revert HEAD
Getting Help
Migration Support
Having trouble migrating?
- 📖 Check our Migration Checklist
- 💬 Ask in Discord #migrations
- 📧 Email: [email protected] (include "Migration v2" in subject)
Report Issues
Found a bug after migrating?
- 🐛 Check Known Issues
- 📋 File issue: [email protected]
- Include:
- Version migrating from/to
- Error messages
- Code snippets
- Browser/environment details
Known Issues
Issue: Toast Not Showing
Cause: Custom CSS conflicting with toast styles
Fix:
// Override toast styles
LoyalteezAutomation.showRewardToast = function(amount, eventType, userEmail) {
const toast = document.createElement('div');
toast.style.cssText = `
position: fixed !important;
z-index: 999999 !important;
/* ... rest of styles */
`;
// ... rest of implementation
};
Issue: Rate Limit Errors After Upgrade
Cause: v2 has stricter deduplication (2s window)
Fix: This is expected behavior. The SDK prevents duplicate events more aggressively.
// If you need to retry sooner:
setTimeout(() => {
LoyalteezAutomation.track(eventType, data);
}, 2500); // Wait 2.5s
Issue: Privy Token Errors
Cause: Token expired or not refreshed
Fix:
// Always get fresh token
const token = await privy.getAccessToken();
// Don't cache tokens
Best Practices
1. Test in Staging First
// Use staging Brand ID
const BRAND_ID = process.env.NODE_ENV === 'production'
? 'prod_brand_id'
: 'staging_brand_id';
LoyalteezAutomation.init(BRAND_ID, options);
2. Gradual Rollout
// Feature flag
const USE_V2 = localStorage.getItem('loyalteez_v2') === 'true';
if (USE_V2) {
// Use v2 format
} else {
// Use v1 format
}
3. Monitor Errors
// Track migration errors
try {
await LoyalteezAutomation.track(eventType, data);
} catch (error) {
// Log to monitoring service
Sentry.captureException(error, {
tags: { version: 'v2', migration: true }
});
}
Current Feature Set
v2.0 includes all production-ready features:
| Feature | Status |
|---|---|
| Event Tracking | ✅ |
| Auto-Detection | ✅ |
| Gas Relayer | ✅ |
| OAuth Pregeneration | ✅ |
| Idempotent APIs | ✅ |
| Toast Notifications | ✅ |
| EIP-2612 Permits | ✅ |
| TypeScript Definitions | ✅ |
| Enhanced Error Messages | ✅ |
Related Documentation
Questions? Join our Discord or email [email protected]