Skip to main content

contactsViral

Opens the contacts-based viral sharing reward module. When the user completes sharing with a friend, the reward info is delivered via the onEvent callback. A close event is fired when the module exits.

Signature

import { contactsViral } from '@apps-in-toss/web-framework';

declare function contactsViral(params: ContactsViralParams): () => void;

interface ContactsViralParams {
options: ContactsViralOption;
onEvent: (event: ContactsViralEvent) => void;
onError: (error: unknown) => void;
}

interface ContactsViralOption {
/** UUID-format module ID from the Apps in Toss Console > Mini App > Share Reward menu */
moduleId: string;
}

type ContactsViralEvent = RewardFromContactsViralEvent | ContactsViralSuccessEvent;

type RewardFromContactsViralEvent = {
type: 'sendViral';
data: {
rewardAmount: number;
rewardUnit: string;
};
};

type ContactsViralSuccessEvent = {
type: 'close';
data: {
closeReason: 'clickBackButton' | 'noReward';
sentRewardAmount?: number;
sendableRewardsCount?: number;
sentRewardsCount: number;
rewardUnit?: string;
};
};

Parameters

NameTypeRequiredDescription
params.options.moduleIdstringUUID-format module ID from the Apps in Toss Console > Mini App > Share Reward menu.
params.onEvent(event: ContactsViralEvent) => voidEvent callback. type: 'sendViral' fires on share completion; type: 'close' fires when the module exits.
params.onError(error: unknown) => voidCalled on unexpected errors.

Returns

  • () => void — App bridge cleanup function. Must be called when sharing is done or the component unmounts to release resources.

Event details

type: 'sendViral' — sharing completed

Fires when the user successfully shares with a friend.

FieldTypeDescription
data.rewardAmountnumberReward quantity granted (value configured in the Console)
data.rewardUnitstringReward unit name (e.g. '하트', '보석')

type: 'close' — module closed

Fires when the sharing module is dismissed.

FieldTypeDescription
data.closeReason'clickBackButton' | 'noReward'Why the module closed: back button press, or no rewards remaining.
data.sentRewardsCountnumberNumber of friends shared with
data.sentRewardAmountnumber | undefinedTotal rewards earned
data.sendableRewardsCountnumber | undefinedFriends still available to share with
data.rewardUnitstring | undefinedReward unit name

Permission

Requires the contacts permission. If the status is explicitly denied, the module may fail — handle this in the onError callback. See Guides — Permissions pattern for the full flow.

contactsViral does not expose getPermission / openPermissionDialog directly on the function object. Use fetchContacts.getPermission(), which shares the same contacts permission:

import { fetchContacts, contactsViral } from '@apps-in-toss/web-framework';

// fetchContacts and contactsViral share the same 'contacts' permission.
const status = await fetchContacts.getPermission();
if (status === 'denied') {
await fetchContacts.openPermissionDialog();
}

Examples

Minimal

import { contactsViral } from '@apps-in-toss/web-framework';
import { useEffect } from 'react';

function ViralButton({ moduleId }: { moduleId: string }) {
useEffect(() => {
const cleanup = contactsViral({
options: { moduleId },
onEvent: (event) => {
if (event.type === 'sendViral') {
console.log('Reward granted:', event.data.rewardAmount, event.data.rewardUnit);
} else if (event.type === 'close') {
console.log('Module closed:', event.data.closeReason);
}
},
onError: (error) => {
console.error('Error:', error);
},
});

return cleanup;
}, [moduleId]);

return <button type="button">Share with a friend and earn a reward</button>;
}

Realistic — display reward status after sharing

import { contactsViral } from '@apps-in-toss/web-framework';
import { useState } from 'react';

interface RewardState {
totalEarned: number;
unit: string;
}

export function ContactsViralSection({ moduleId }: { moduleId: string }) {
const [reward, setReward] = useState<RewardState | null>(null);
const [errorMessage, setErrorMessage] = useState('');

function handleStart() {
const cleanup = contactsViral({
options: { moduleId },
onEvent: (event) => {
if (event.type === 'sendViral') {
setReward({
totalEarned: event.data.rewardAmount,
unit: event.data.rewardUnit,
});
} else if (event.type === 'close') {
console.log('Close reason:', event.data.closeReason, '/ shares sent:', event.data.sentRewardsCount);
}
},
onError: (error) => {
setErrorMessage('Something went wrong. Please try again.');
console.error(error);
cleanup?.();
},
});
}

return (
<div>
<button type="button" onClick={handleStart}>
Share with a friend and earn a reward
</button>
{reward && (
<p role="status">
Reward earned: {reward.totalEarned} {reward.unit}
</p>
)}
{errorMessage && <p role="alert">{errorMessage}</p>}
</div>
);
}

Try it live

contactsViral cannot be run directly in sdk-example (it requires a moduleId issued from the Console).

Open in sdk-example

External references