Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[guides] add Prompting Users to Share Incentivized Links Guide #7342

Merged
merged 9 commits into from
Feb 18, 2025
97 changes: 93 additions & 4 deletions docs/activities/Development_Guides.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ These guides include suggested development practices, SDK commands, and user flo
</Card>
</Container>

## Growth and Referrals
<Container>
<Card title="Prompting Users to Share Incentivized Links" link="#DOCS_ACTIVITIES_DEVELOPMENT_GUIDES/prompting-users-to-share-incentivized-links">
Encourage your users to share links to your activity by adding tracking and offering rewards for engagement.
</Card>
</Container>

## Assets & Metadata
<Container>
<Card title="Setting Up Activity Metadata" link="#DOCS_ACTIVITIES_DEVELOPMENT_GUIDES/setting-up-activity-metadata">
Expand Down Expand Up @@ -812,7 +819,7 @@ Here's a basic example for retrieving a user's avatar and username

```javascript
// We'll be referencing the user object returned from authenticate
const {user} = await DiscordRPC.commands.authenticate({
const {user} = await discordSdk.commands.authenticate({
access_token: accessToken,
});

Expand All @@ -839,13 +846,13 @@ Here's an example of how to retrieve the user's guild-specific avatar and nickna

```javascript
// We'll be referencing the user object returned from authenticate
const {user} = await DiscordRPC.commands.authenticate({
const {user} = await discordSdk.commands.authenticate({
access_token: accessToken,
});

// When using the proxy, you may instead replace `https://discord.com` with `/discord`
// or whatever url mapping you have chosen via the developer portal
fetch(`https://discord.com/api/users/@me/guilds/${DiscordRPC.guildId}/member`, {
fetch(`https://discord.com/api/users/@me/guilds/${discordSdk.guildId}/member`, {
method: 'GET',
headers: {
Authorization: `Bearer ${accessToken}`,
Expand All @@ -858,7 +865,7 @@ fetch(`https://discord.com/api/users/@me/guilds/${DiscordRPC.guildId}/member`, {
let guildAvatarSrc = '';
// Retrieve the guild-specific avatar, and fallback to the user's avatar
if (guildsMembersRead?.avatar) {
guildAvatarSrc = `https://cdn.discordapp.com/guilds/${DiscordRPC.guildId}/users/${user.id}/avatars/${guildsMembersRead.avatar}.png?size=256`;
guildAvatarSrc = `https://cdn.discordapp.com/guilds/${discordSdk.guildId}/users/${user.id}/avatars/${guildsMembersRead.avatar}.png?size=256`;
} else if (user.avatar) {
guildAvatarSrc = `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png?size=256`;
} else {
Expand All @@ -879,6 +886,88 @@ This example is being done entirely on the client, however, a more common patter

---

### Prompting Users to Share Incentivized Links

Incentivized sharing can help grow your Activity through network effects. This guide covers implementing a reward system for users who share links and those who click them.

#### Implementation Overview
1. Create and track an incentivized link for a promotional campaign, then prompt users to share the link
2. Handle incoming referrals and grant valid rewards

#### Sharing Links

When implementing sharing, you'll need to:
1. Generate a unique ID for tracking the promotion
2. Call the [`shareLink`](#DOCS_DEVELOPER_TOOLS_EMBEDDED_APP_SDK/sharelink) command
3. Track the share attempt

```javascript
// Generate a unique ID for this promotion
// This could be per-campaign, per-user, or per-share depending on your needs
const customId = await createPromotionalCustomId();

try {
const { success } = await discordSdk.commands.shareLink({
message: 'Click this link to redeem 5 free coins!',
custom_id: customId,
// referrer_id is optional - if omitted, the current user's ID is used
});

if (success) {
// Track successful share for analytics/limiting
await trackSuccessfulShare(customId);
Comment on lines +916 to +918

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a user clicks a "copy link" button in the modal but then closes it without doing on-platform sharing, what is the value of success?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

success will be true upon copy

}
} catch (error) {
// Handle share failures appropriately
console.error('Failed to share link:', error);
}
```

#### Handling Incoming Referrals
When a user clicks a shared link, your activity will launch with referral data available through the SDK:

```javascript
// Early in your activity's initialization
async function handleReferral() {
// Validate the referral data
if (!discordSdk.customId || !discordSdk.referrerId) {
return;
}

try {
// Verify this is a valid promotion and hasn't expired
const promotion = await validatePromotion(discordSdk.customId);
if (!promotion) {
console.log('Invalid or expired promotion');
return;
}

// Prevent self-referrals
if (discordSdk.referrerId === currentUserId) {
console.log('Self-referrals not allowed');
return;
}

// Grant rewards to both users
await grantRewards({
promotionId: discordSdk.customId,
referrerId: discordSdk.referrerId,
newUserId: currentUserId
});
} catch (error) {
console.error('Failed to process referral:', error);
}
}
```

#### Link Sharing Best Practices
- Generate unique, non-guessable `customId`s
- Track and validate referrals to prevent abuse
- Handle edge cases like expired promotions gracefully
- Consider implementing cool-down periods between shares

---

### Preventing unwanted activity sessions

Activities are surfaced through iframes in the Discord app. The activity website itself is publicly reachable at `<application_id>.discordsays.com`. Activities will expect to be able to communicate with Discord's web or mobile client via the Discord SDK's RPC protocol. If a user loads the activity's website in a normal browser, the Discord RPC server will not be present, and the activity will likely fail in some way.
Expand Down