This guide walks you through migrating your existing user base to Snag’s loyalty system. Whether you’re moving from another platform or setting up Snag for the first time, this guide will help you successfully migrate your users.
Prerequisites
Before starting your migration:
Export your user data from your existing system
Migration Strategy
Follow these steps to migrate your users systematically:
Prepare your user data
Export your user data and ensure you have wallet addresses for each user. Users without wallet addresses cannot be created in Snag. If your users don’t have wallet addresses, see our wallet generation guide for guidance on how to create them. Required Data
walletAddress (required) - The unique blockchain wallet address
externalIdentifier - Your internal user ID (highly recommended)
displayName - User’s display name
emailAddress - User’s email address
discordUser - Discord username
discordUserId - Discord user ID
twitterUser - Twitter/X username
twitterUserId - Twitter/X user ID
telegramUsername - Telegram username
telegramUserId - Telegram user ID
logoUrl - URL to user’s avatar/profile picture
Set up your migration script
Create a script to process your users in batches. Here’s a complete example: import SnagSolutions from '@snagsolutions/sdk'
const client = new SnagSolutions ({
apiKey: process . env . SNAG_API_KEY ,
})
interface YourUser {
id : string
walletAddress : string
name : string
email : string
// ... other fields
}
async function migrateUsers ( users : YourUser []) {
const results = {
successful: 0 ,
failed: 0 ,
errors: [] as Array <{ userId : string ; error : string }>,
}
for ( const user of users ) {
try {
await client . users . createMetadata ({
walletAddress: user . walletAddress ,
externalIdentifier: user . id ,
displayName: user . name ,
emailAddress: user . email ,
})
results . successful ++
console . log ( `✓ Migrated user ${ user . id } ` )
} catch ( error ) {
results . failed ++
results . errors . push ({
userId: user . id ,
error: error . message ,
})
console . error ( `✗ Failed to migrate user ${ user . id } :` , error . message )
}
}
return results
}
// Usage
const users = await fetchYourUsers ()
const results = await migrateUsers ( users )
console . log ( `Migration complete: ${ results . successful } successful, ${ results . failed } failed` )
Process in batches
For large migrations, process users in batches to avoid rate limits and make the process more manageable: async function migrateInBatches (
users : YourUser [],
batchSize : number = 100 ,
delayMs : number = 1000
) {
const totalBatches = Math . ceil ( users . length / batchSize )
for ( let i = 0 ; i < totalBatches ; i ++ ) {
const start = i * batchSize
const end = start + batchSize
const batch = users . slice ( start , end )
console . log ( `Processing batch ${ i + 1 } / ${ totalBatches } ` )
await migrateUsers ( batch )
// Add delay between batches to respect rate limits
if ( i < totalBatches - 1 ) {
await new Promise ( resolve => setTimeout ( resolve , delayMs ))
}
}
}
Start with a small batch (10-50 users) to verify your migration script works correctly before processing your entire user base.
Verify migration
After migration, verify that all users were created successfully: async function verifyMigration ( externalIds : string []) {
const notFound : string [] = []
for ( const externalId of externalIds ) {
try {
// Query by external identifier
const users = await client . users . list ({
externalIdentifier: externalId ,
})
if ( users . length === 0 ) {
notFound . push ( externalId )
}
} catch ( error ) {
console . error ( `Error checking user ${ externalId } :` , error )
notFound . push ( externalId )
}
}
if ( notFound . length > 0 ) {
console . log ( `Missing users: ${ notFound . length } ` )
console . log ( notFound )
} else {
console . log ( '✓ All users verified successfully' )
}
return notFound
}
Handle failed migrations
Review and retry failed migrations: async function retryFailed ( errors : Array <{ userId : string ; error : string }>) {
console . log ( `Retrying ${ errors . length } failed migrations...` )
const usersToRetry = await fetchUsersById ( errors . map ( e => e . userId ))
const results = await migrateUsers ( usersToRetry )
return results
}
Common errors and solutions:
Invalid wallet address - Verify the wallet address format
Duplicate user - User already exists, use update instead
Rate limit - Increase delay between batches
Authentication error - Check your API key
Migration Best Practices
Before migrating your entire user base, test with a small subset (10-100 users) to identify any issues with your data format or migration script.
Always include the externalIdentifier field mapping to your internal user ID. This makes it easy to:
Query Snag users from your system
Link Snag data back to your users
Debug migration issues
Avoid duplicate migrations
Log all migration attempts with timestamps, user IDs, and results. This helps with debugging and provides an audit trail.
Have a rollback strategy in case something goes wrong. Keep track of which users were migrated so you can clean up if needed.
The metadata endpoint is idempotent - calling it multiple times with the same wallet address updates the user. This means you can safely re-run your migration script.
Watch for errors, rate limits, and performance issues during migration. Be prepared to pause and adjust your approach if needed.
Handling Users Without Wallet Addresses
If some of your users don’t have wallet addresses, you have several options:
Generate Wallets Generate wallet addresses programmatically using viem or smart wallet providers
Prompt Users Ask users to connect their wallets during their next login
Gradual Migration Migrate users as they connect wallets, rather than all at once
Email-Based Use email-to-wallet services to create wallets tied to email addresses
Complete Migration Example
Here’s a complete, production-ready migration script:
import SnagSolutions from '@snagsolutions/sdk'
import { writeFileSync } from 'fs'
const client = new SnagSolutions ({
apiKey: process . env . SNAG_API_KEY ,
})
interface MigrationResult {
timestamp : string
totalUsers : number
successful : number
failed : number
errors : Array <{ userId : string ; error : string }>
}
async function runMigration () : Promise < MigrationResult > {
console . log ( 'Starting user migration...' )
// Fetch your users
const users = await fetchYourUsers ()
console . log ( `Found ${ users . length } users to migrate` )
const result : MigrationResult = {
timestamp: new Date (). toISOString (),
totalUsers: users . length ,
successful: 0 ,
failed: 0 ,
errors: [],
}
// Process in batches
const batchSize = 100
const totalBatches = Math . ceil ( users . length / batchSize )
for ( let i = 0 ; i < totalBatches ; i ++ ) {
const start = i * batchSize
const end = start + batchSize
const batch = users . slice ( start , end )
console . log ( ` \n Processing batch ${ i + 1 } / ${ totalBatches } ( ${ batch . length } users)` )
for ( const user of batch ) {
try {
await client . users . createMetadata ({
walletAddress: user . walletAddress ,
externalIdentifier: user . id ,
displayName: user . name ,
emailAddress: user . email ,
discordUser: user . discordUsername ,
twitterUser: user . twitterUsername ,
})
result . successful ++
process . stdout . write ( '.' )
} catch ( error ) {
result . failed ++
result . errors . push ({
userId: user . id ,
error: error . message ,
})
process . stdout . write ( '✗' )
}
}
// Rate limit protection
if ( i < totalBatches - 1 ) {
await new Promise ( resolve => setTimeout ( resolve , 1000 ))
}
}
// Save results
const filename = `migration- ${ Date . now () } .json`
writeFileSync ( filename , JSON . stringify ( result , null , 2 ))
console . log ( ` \n\n Migration complete!` )
console . log ( `✓ Successful: ${ result . successful } ` )
console . log ( `✗ Failed: ${ result . failed } ` )
console . log ( `Results saved to: ${ filename } ` )
return result
}
// Run migration
runMigration ()
. then (() => process . exit ( 0 ))
. catch ( error => {
console . error ( 'Migration failed:' , error )
process . exit ( 1 )
})
Next Steps
Generate Wallet Addresses Learn how to generate wallet addresses for users who don’t have them
Manage User Groups Connect multiple wallets to a single user using user groups
Connect Social Accounts Integrate social media platforms with your users
Create Users Learn more about creating and managing individual users