Overview
DRIP’s quest system allows you to create structured challenges and tasks that members can complete to earn rewards. Quests can be simple one-time tasks or complex multi-step adventures.Quests in DRIP are highly customizable and can integrate with external systems, making them perfect for building comprehensive gamification strategies.
Quest Structure
Understanding Quest Components
Components:- Quest: The overall challenge or objective
- Tasks: Individual steps within the quest
- Triggers: Events that activate task checking
- Conditions: Requirements that must be met
- Actions: What happens when tasks are completed
Reading Quest Data
Get Available Quests
- JavaScript
- Python
Copy
async function getAvailableQuests(client, memberId = null) {
const endpoint = memberId
? `/realm/${client.realmId}/members/${memberId}/quests`
: `/realm/${client.realmId}/quests`;
const quests = await client.request('GET', endpoint);
return quests.data.map(quest => ({
id: quest.id,
name: quest.name,
description: quest.description,
status: quest.status,
tasks: quest.tasks?.length || 0,
rewards: quest.rewards || [],
startDate: quest.startDate,
endDate: quest.endDate
}));
}
// Get all quests in the realm
const allQuests = await getAvailableQuests(client);
// Get quests for a specific member (with progress)
const memberQuests = await getAvailableQuests(client, 'member123');
Copy
def get_available_quests(client, member_id=None):
endpoint = (f'/realm/{client.realm_id}/members/{member_id}/quests'
if member_id else f'/realm/{client.realm_id}/quests')
quests = client.request('GET', endpoint)
return [{
'id': quest['id'],
'name': quest['name'],
'description': quest['description'],
'status': quest['status'],
'tasks': len(quest.get('tasks', [])),
'rewards': quest.get('rewards', []),
'start_date': quest.get('startDate'),
'end_date': quest.get('endDate')
} for quest in quests['data']]
# Get all quests
all_quests = get_available_quests(client)
# Get member-specific quests
member_quests = get_available_quests(client, 'member123')
Get Quest Details
Copy
async function getQuestDetails(client, questId) {
const quest = await client.request('GET',
`/realm/${client.realmId}/quests/${questId}`
);
return {
id: quest.id,
name: quest.name,
description: quest.description,
status: quest.status,
tasks: quest.tasks.map(task => ({
id: task.id,
name: task.name,
description: task.description,
type: task.type,
conditions: task.conditions,
rewards: task.rewards,
isCompleted: task.isCompleted
})),
totalRewards: quest.rewards,
participants: quest.participants || 0,
completionRate: quest.completionRate || 0
};
}
Quest Progress Tracking
Check Member Quest Progress
Copy
async function getMemberQuestProgress(client, memberId, questId = null) {
const endpoint = questId
? `/realm/${client.realmId}/members/${memberId}/quests/${questId}`
: `/realm/${client.realmId}/members/${memberId}/quests`;
const progress = await client.request('GET', endpoint);
if (questId) {
// Single quest progress
return {
questId: progress.questId,
status: progress.status,
completedTasks: progress.completedTasks,
totalTasks: progress.totalTasks,
progress: progress.progress, // 0-1
startedAt: progress.startedAt,
completedAt: progress.completedAt,
taskProgress: progress.tasks?.map(task => ({
taskId: task.id,
name: task.name,
isCompleted: task.isCompleted,
completedAt: task.completedAt
}))
};
} else {
// All quest progress for member
return progress.data.map(quest => ({
questId: quest.questId,
questName: quest.questName,
status: quest.status,
progress: quest.progress,
completedTasks: quest.completedTasks,
totalTasks: quest.totalTasks
}));
}
}
// Usage
const allProgress = await getMemberQuestProgress(client, 'member123');
const specificProgress = await getMemberQuestProgress(client, 'member123', 'quest456');
Quest Analytics
Copy
class QuestAnalytics {
constructor(client) {
this.client = client;
}
async getQuestStats(questId) {
const quest = await this.client.request('GET',
`/realm/${this.client.realmId}/quests/${questId}/analytics`
);
return {
totalParticipants: quest.totalParticipants,
completedCount: quest.completedCount,
completionRate: quest.completionRate,
averageCompletionTime: quest.averageCompletionTime,
taskCompletionRates: quest.taskCompletionRates,
dropOffPoints: quest.dropOffPoints // Where people stop
};
}
async getPopularQuests(limit = 10) {
const quests = await this.client.request('GET',
`/realm/${this.client.realmId}/quests/analytics/popular?limit=${limit}`
);
return quests.data.map(quest => ({
id: quest.id,
name: quest.name,
participants: quest.participants,
completionRate: quest.completionRate,
averageRating: quest.averageRating
}));
}
async getMemberQuestHistory(memberId) {
const history = await this.client.request('GET',
`/realm/${this.client.realmId}/members/${memberId}/quests/history`
);
return history.data.map(entry => ({
questId: entry.questId,
questName: entry.questName,
status: entry.status,
startedAt: entry.startedAt,
completedAt: entry.completedAt,
rewardsEarned: entry.rewardsEarned
}));
}
}
External Quest Integration
Triggering Quest Progress
For external integrations, you can manually trigger quest progress:Copy
async function triggerQuestProgress(client, memberId, questId, taskId, data = {}) {
const result = await client.request('POST',
`/realm/${client.realmId}/members/${memberId}/quests/${questId}/tasks/${taskId}/trigger`,
{
triggerData: data,
timestamp: new Date().toISOString()
}
);
return result;
}
// Example: Trigger progress when member completes external action
await triggerQuestProgress(client, 'member123', 'quest456', 'task789', {
actionType: 'external_purchase',
amount: 99.99,
productId: 'premium-subscription'
});
Custom Quest Conditions
Check if custom conditions are met:Copy
async function checkCustomCondition(client, memberId, conditionType, conditionData) {
switch (conditionType) {
case 'external_api_check':
return await checkExternalAPI(memberId, conditionData);
case 'social_media_follow':
return await checkSocialMediaFollow(memberId, conditionData);
case 'website_visit':
return await checkWebsiteVisit(memberId, conditionData);
case 'email_verification':
return await checkEmailVerification(memberId);
default:
return false;
}
}
async function checkExternalAPI(memberId, conditionData) {
try {
const response = await fetch(conditionData.apiUrl, {
headers: {
'Authorization': `Bearer ${conditionData.apiKey}`,
'Content-Type': 'application/json'
}
});
const data = await response.json();
// Check if condition is met based on API response
return data.userStatus === conditionData.requiredStatus;
} catch (error) {
console.error('External API check failed:', error);
return false;
}
}
Building Quest-Driven Features
Quest Dashboard
Create a quest dashboard for members:Copy
class QuestDashboard {
constructor(client, memberId) {
this.client = client;
this.memberId = memberId;
}
async getDashboardData() {
const [activeQuests, completedQuests, availableQuests] = await Promise.all([
this.getActiveQuests(),
this.getCompletedQuests(),
this.getAvailableQuests()
]);
return {
activeQuests,
completedQuests,
availableQuests,
stats: await this.getQuestStats()
};
}
async getActiveQuests() {
const quests = await getMemberQuestProgress(this.client, this.memberId);
return quests.filter(quest => quest.status === 'active');
}
async getCompletedQuests() {
const analytics = new QuestAnalytics(this.client);
const history = await analytics.getMemberQuestHistory(this.memberId);
return history.filter(quest => quest.status === 'completed');
}
async getAvailableQuests() {
const allQuests = await getAvailableQuests(this.client);
const memberQuests = await getMemberQuestProgress(this.client, this.memberId);
const memberQuestIds = new Set(memberQuests.map(q => q.questId));
return allQuests.filter(quest =>
quest.status === 'active' && !memberQuestIds.has(quest.id)
);
}
async getQuestStats() {
const history = await new QuestAnalytics(this.client)
.getMemberQuestHistory(this.memberId);
const completed = history.filter(q => q.status === 'completed');
const totalRewards = completed.reduce((sum, quest) =>
sum + (quest.rewardsEarned?.points || 0), 0
);
return {
totalCompleted: completed.length,
totalStarted: history.length,
completionRate: history.length > 0 ? completed.length / history.length : 0,
totalRewardsEarned: totalRewards,
currentStreak: await this.getCurrentStreak()
};
}
async getCurrentStreak() {
// Calculate current daily quest completion streak
const history = await new QuestAnalytics(this.client)
.getMemberQuestHistory(this.memberId);
const dailyQuests = history.filter(q =>
q.questName.toLowerCase().includes('daily') && q.status === 'completed'
);
// Calculate streak logic here
return this.calculateStreak(dailyQuests);
}
calculateStreak(completedQuests) {
if (completedQuests.length === 0) return 0;
// Sort by completion date (most recent first)
const sorted = completedQuests.sort((a, b) =>
new Date(b.completedAt) - new Date(a.completedAt)
);
let streak = 0;
let currentDate = new Date();
for (const quest of sorted) {
const completedDate = new Date(quest.completedAt);
const daysDiff = Math.floor((currentDate - completedDate) / (1000 * 60 * 60 * 24));
if (daysDiff <= streak + 1) {
streak++;
currentDate = completedDate;
} else {
break;
}
}
return streak;
}
}
Quest Notifications
Set up notifications for quest events:Copy
class QuestNotificationSystem {
constructor(client) {
this.client = client;
this.subscribers = new Map();
}
subscribe(memberId, notificationTypes) {
this.subscribers.set(memberId, notificationTypes);
}
async handleQuestEvent(event) {
const { type, data } = event;
switch (type) {
case 'quest.started':
await this.notifyQuestStarted(data);
break;
case 'quest.completed':
await this.notifyQuestCompleted(data);
break;
case 'task.completed':
await this.notifyTaskCompleted(data);
break;
case 'quest.expired':
await this.notifyQuestExpired(data);
break;
}
}
async notifyQuestStarted(data) {
const { member, quest } = data;
if (this.shouldNotify(member.id, 'quest.started')) {
await this.sendNotification(member.id, {
title: 'Quest Started!',
message: `You've started "${quest.name}". Good luck!`,
type: 'success',
questId: quest.id
});
}
}
async notifyQuestCompleted(data) {
const { member, quest, rewards } = data;
if (this.shouldNotify(member.id, 'quest.completed')) {
const rewardText = rewards.map(r =>
`${r.amount} ${r.realmPoint?.emoji || 'points'}`
).join(', ');
await this.sendNotification(member.id, {
title: 'Quest Completed! 🎉',
message: `Congratulations! You completed "${quest.name}" and earned ${rewardText}!`,
type: 'celebration',
questId: quest.id
});
}
}
shouldNotify(memberId, eventType) {
const preferences = this.subscribers.get(memberId);
return preferences && preferences.includes(eventType);
}
async sendNotification(memberId, notification) {
// Implement your notification delivery method
// Could be Discord DM, email, push notification, etc.
console.log(`📬 Notification for ${memberId}:`, notification);
}
}
Advanced Quest Patterns
Dynamic Quest Generation
Create quests programmatically based on member behavior:Copy
class DynamicQuestGenerator {
constructor(client) {
this.client = client;
}
async generatePersonalizedQuest(memberId) {
// Analyze member activity
const memberStats = await this.analyzeMemberActivity(memberId);
// Generate quest based on their patterns
const questTemplate = this.selectQuestTemplate(memberStats);
// Create personalized quest
const quest = await this.createQuest(questTemplate, memberStats);
return quest;
}
async analyzeMemberActivity(memberId) {
const analytics = new QuestAnalytics(this.client);
const history = await analytics.getMemberQuestHistory(memberId);
return {
preferredQuestTypes: this.getPreferredTypes(history),
averageCompletionTime: this.getAverageCompletionTime(history),
lastActiveDate: this.getLastActiveDate(history),
completionRate: this.getCompletionRate(history),
favoriteRewards: this.getFavoriteRewards(history)
};
}
selectQuestTemplate(memberStats) {
// Select quest template based on member preferences
if (memberStats.preferredQuestTypes.includes('daily')) {
return this.getDailyQuestTemplate();
} else if (memberStats.preferredQuestTypes.includes('social')) {
return this.getSocialQuestTemplate();
} else {
return this.getGeneralQuestTemplate();
}
}
async createQuest(template, memberStats) {
// Customize template with member-specific parameters
const questData = {
...template,
name: this.personalizeName(template.name, memberStats),
description: this.personalizeDescription(template.description, memberStats),
rewards: this.personalizeRewards(template.rewards, memberStats),
difficulty: this.calculateDifficulty(memberStats)
};
// Create quest via API
return await this.client.request('POST',
`/realm/${this.client.realmId}/quests`,
questData
);
}
}
Quest Chains and Series
Create interconnected quest series:Copy
class QuestChainManager {
constructor(client) {
this.client = client;
this.chains = new Map();
}
async createQuestChain(chainConfig) {
const chain = {
id: chainConfig.id,
name: chainConfig.name,
description: chainConfig.description,
quests: [],
requirements: chainConfig.requirements || {}
};
// Create individual quests in the chain
for (let i = 0; i < chainConfig.quests.length; i++) {
const questConfig = chainConfig.quests[i];
// Add prerequisite requirements (previous quest must be completed)
if (i > 0) {
questConfig.prerequisites = [chain.quests[i - 1].id];
}
const quest = await this.client.request('POST',
`/realm/${this.client.realmId}/quests`,
questConfig
);
chain.quests.push(quest);
}
this.chains.set(chain.id, chain);
return chain;
}
async getChainProgress(chainId, memberId) {
const chain = this.chains.get(chainId);
if (!chain) throw new Error('Chain not found');
const progress = [];
for (const quest of chain.quests) {
const questProgress = await getMemberQuestProgress(
this.client, memberId, quest.id
);
progress.push({
questId: quest.id,
questName: quest.name,
status: questProgress.status,
progress: questProgress.progress,
isUnlocked: await this.isQuestUnlocked(quest.id, memberId)
});
}
return {
chainId,
chainName: chain.name,
totalQuests: chain.quests.length,
completedQuests: progress.filter(p => p.status === 'completed').length,
currentQuest: progress.find(p => p.status === 'active'),
nextQuest: progress.find(p => p.isUnlocked && p.status === 'available'),
progress
};
}
async isQuestUnlocked(questId, memberId) {
// Check if prerequisites are met
const quest = await this.client.request('GET',
`/realm/${this.client.realmId}/quests/${questId}`
);
if (!quest.prerequisites || quest.prerequisites.length === 0) {
return true;
}
// Check if all prerequisite quests are completed
for (const prereqId of quest.prerequisites) {
const prereqProgress = await getMemberQuestProgress(
this.client, memberId, prereqId
);
if (prereqProgress.status !== 'completed') {
return false;
}
}
return true;
}
}
Quest Performance Optimization
Caching Quest Data
Copy
class CachedQuestManager {
constructor(client, cacheTtl = 300000) { // 5 minutes
this.client = client;
this.cacheTtl = cacheTtl;
this.questCache = new Map();
this.progressCache = new Map();
}
async getQuest(questId) {
const cacheKey = `quest:${questId}`;
const cached = this.questCache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < this.cacheTtl) {
return cached.data;
}
const quest = await getQuestDetails(this.client, questId);
this.questCache.set(cacheKey, {
data: quest,
timestamp: Date.now()
});
return quest;
}
async getMemberProgress(memberId, questId) {
const cacheKey = `progress:${memberId}:${questId}`;
const cached = this.progressCache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < this.cacheTtl) {
return cached.data;
}
const progress = await getMemberQuestProgress(this.client, memberId, questId);
this.progressCache.set(cacheKey, {
data: progress,
timestamp: Date.now()
});
return progress;
}
invalidateCache(type, id) {
if (type === 'quest') {
this.questCache.delete(`quest:${id}`);
} else if (type === 'progress') {
// Invalidate all progress entries for this member
const keysToDelete = Array.from(this.progressCache.keys())
.filter(key => key.startsWith(`progress:${id}:`));
keysToDelete.forEach(key => this.progressCache.delete(key));
}
}
}
Best Practices
Quest Design
Quest Design
Design engaging and achievable quests:
Copy
const questDesignPrinciples = {
clarity: {
name: "Clear, descriptive quest names",
description: "Detailed but concise descriptions",
tasks: "Specific, measurable tasks"
},
progression: {
difficulty: "Gradual difficulty increase",
rewards: "Meaningful and proportional rewards",
feedback: "Clear progress indicators"
},
engagement: {
variety: "Mix of different quest types",
personalization: "Tailored to member preferences",
social: "Include collaborative elements"
}
};
Performance
Performance
Optimize quest system performance:
Copy
// Batch quest progress checks
async function batchCheckProgress(client, memberIds, questId) {
const promises = memberIds.map(memberId =>
getMemberQuestProgress(client, memberId, questId)
);
return await Promise.all(promises);
}
// Use efficient data structures
const questProgressMap = new Map();
memberProgresses.forEach(progress => {
questProgressMap.set(progress.memberId, progress);
});
Error Handling
Error Handling
Handle quest operations gracefully:
Copy
async function safeQuestOperation(operation, fallback = null) {
try {
return await operation();
} catch (error) {
console.error('Quest operation failed:', error.message);
// Log error for debugging
await logQuestError(error);
// Return fallback value
return fallback;
}
}
Next Steps
API Reference
Explore quest-related API endpoints and advanced features
Point Systems
Learn to create reward systems for quest completion
Member Management
Manage quest participants and track progress
Best Practices
Follow production-ready patterns for quest systems
Building quest systems? Join our Discord community to share your quest designs and get feedback from other developers! 🗺️

