Marketplace platforms face unique email challenges: multiple parties, complex transaction flows, and the need to maintain trust between strangers. Here's how to design effective multi-party notification systems.
The marketplace email challenge
Unlike single-party applications, marketplaces must communicate with:
- —Buyers making purchases
- —Sellers fulfilling orders
- —The platform itself (for disputes, policies, etc.)
Each party needs different information at different times, and emails must build trust while protecting privacy.
Core notification types
Order lifecycle emails
To buyer:
Order confirmed → Payment processed → Shipped → Delivered → Review request
To seller:
New order → Payment received → Ship reminder → Delivery confirmed → Payout processed
Example: Order confirmation (buyer)
interface BuyerOrderEmail {
buyer: {
name: string;
email: string;
};
order: {
id: string;
items: OrderItem[];
total: number;
estimatedDelivery: string;
};
seller: {
storeName: string;
// Note: No seller email or personal details
};
}
const buyerOrderTemplate = `
Hi {{buyer.name}},
Your order #{{order.id}} is confirmed!
Items from {{seller.storeName}}:
{{#each order.items}}
- {{this.name}} × {{this.quantity}}: ${{this.price}}
{{/each}}
Total: ${{order.total}}
Estimated delivery: {{order.estimatedDelivery}}
Track your order: {{trackingUrl}}
`;
Example: New order (seller)
interface SellerOrderEmail {
seller: {
name: string;
email: string;
};
order: {
id: string;
items: OrderItem[];
revenue: number;
platformFee: number;
payout: number;
shippingAddress: Address;
shipBy: string;
};
buyer: {
// Only what seller needs
shippingName: string;
};
}
const sellerOrderTemplate = `
Hi {{seller.name}},
New order #{{order.id}}!
Items to ship:
{{#each order.items}}
- {{this.name}} × {{this.quantity}}
{{/each}}
Ship to:
{{buyer.shippingName}}
{{order.shippingAddress.line1}}
{{order.shippingAddress.city}}, {{order.shippingAddress.state}} {{order.shippingAddress.zip}}
Ship by: {{order.shipBy}}
Revenue: ${{order.revenue}}
Platform fee: -${{order.platformFee}}
Your payout: ${{order.payout}}
`;
Privacy-preserving communication
Marketplaces often need to facilitate buyer-seller communication without exposing personal emails.
Masked email relay
// Generate masked addresses
function createMaskedEmail(orderId: string, party: 'buyer' | 'seller'): string {
const token = generateSecureToken(orderId, party);
return `${token}@relay.marketplace.com`;
}
// When buyer wants to contact seller
const buyerMaskedEmail = createMaskedEmail(order.id, 'buyer');
// [email protected]
// When seller wants to contact buyer
const sellerMaskedEmail = createMaskedEmail(order.id, 'seller');
// [email protected]
Relay handling
// Inbound email handler
async function handleRelayEmail(inbound: InboundEmail) {
const { token, party } = parseRelayAddress(inbound.to);
const order = await getOrderByToken(token);
if (!order) {
return sendBounce(inbound.from, 'Invalid relay address');
}
// Determine recipient
const recipient = party === 'buyer'
? order.seller.email
: order.buyer.email;
// Forward with masked reply-to
await sendEmail({
to: recipient,
from: '[email protected]',
replyTo: inbound.to, // Keep the relay address
subject: `Re: Order #${order.id} - ${inbound.subject}`,
html: sanitizeHtml(inbound.html),
headers: {
'X-Marketplace-Order': order.id,
'X-Marketplace-Thread': inbound.headers['message-id']
}
});
// Log for dispute resolution
await logMessage({
orderId: order.id,
from: party === 'buyer' ? 'seller' : 'buyer',
content: inbound.text,
timestamp: new Date()
});
}
Multi-party transaction emails
Escrow and payment flow
// Payment held in escrow
await sendEmail({
to: buyer.email,
template: 'payment-held',
data: {
amount: order.total,
releaseCondition: 'Funds will be released to seller upon delivery confirmation'
}
});
await sendEmail({
to: seller.email,
template: 'payment-pending',
data: {
amount: order.payout,
releaseCondition: 'Ship within 3 days to receive payment'
}
});
// After delivery confirmed
await sendEmail({
to: seller.email,
template: 'payment-released',
data: {
amount: order.payout,
arrivalDate: calculatePayoutDate()
}
});
Dispute notifications
interface DisputeEmail {
disputeId: string;
orderId: string;
reason: string;
filedBy: 'buyer' | 'seller';
deadline: string;
}
// Notify both parties
async function notifyDispute(dispute: DisputeEmail) {
const order = await getOrder(dispute.orderId);
// To the party who filed
await sendEmail({
to: dispute.filedBy === 'buyer' ? order.buyer.email : order.seller.email,
template: 'dispute-filed-confirmation',
data: {
disputeId: dispute.disputeId,
nextSteps: 'We\'ll review and respond within 48 hours'
}
});
// To the other party
await sendEmail({
to: dispute.filedBy === 'buyer' ? order.seller.email : order.buyer.email,
template: 'dispute-notification',
data: {
disputeId: dispute.disputeId,
reason: dispute.reason,
deadline: dispute.deadline,
responseUrl: `${baseUrl}/disputes/${dispute.disputeId}/respond`
}
});
}
Seller onboarding emails
Verification flow
const sellerOnboardingSequence = [
{
trigger: 'signup',
delay: 0,
template: 'seller-welcome',
data: { nextStep: 'Verify your identity' }
},
{
trigger: 'identity-submitted',
delay: 0,
template: 'identity-review',
data: { reviewTime: '1-2 business days' }
},
{
trigger: 'identity-approved',
delay: 0,
template: 'identity-approved',
data: { nextStep: 'Add your first listing' }
},
{
trigger: 'first-listing',
delay: 0,
template: 'listing-live',
data: { tips: 'Optimize your listing for search' }
},
{
trigger: 'first-sale',
delay: 0,
template: 'first-sale-congrats',
data: { payoutInfo: true }
}
];
Payout notifications
// Weekly payout summary
await sendEmail({
to: seller.email,
template: 'payout-summary',
data: {
period: 'Dec 23-29, 2025',
orders: 12,
grossRevenue: 1250.00,
platformFees: 125.00,
refunds: 45.00,
netPayout: 1080.00,
payoutDate: 'Dec 31, 2025',
payoutMethod: 'Bank account ending in 4242'
}
});
Platform communications
Policy updates
// Segment by user type
async function sendPolicyUpdate(policy: PolicyUpdate) {
const affectedUsers = await getUsersByType(policy.affectsUserTypes);
for (const user of affectedUsers) {
await sendEmail({
to: user.email,
template: 'policy-update',
data: {
policyName: policy.name,
effectiveDate: policy.effectiveDate,
summary: policy.summary,
// Customize based on user type
impact: policy.impactByUserType[user.type],
actionRequired: policy.actionRequired[user.type]
}
});
}
}
Trust and safety
// Account warning
await sendEmail({
to: user.email,
template: 'account-warning',
data: {
violation: 'Policy violation detected',
details: 'Your listing "XYZ" was removed for...',
consequence: 'This is your first warning',
appealUrl: `${baseUrl}/appeals/new`
}
});
// Account suspension
await sendEmail({
to: user.email,
template: 'account-suspended',
data: {
reason: 'Multiple policy violations',
pendingOrders: 'Active orders will be cancelled and refunded',
pendingPayouts: 'Payouts are on hold pending review',
appealDeadline: '14 days',
appealUrl: `${baseUrl}/appeals/new`
}
});
Review and rating emails
Review request timing
// Request review after confirmed delivery
async function scheduleReviewRequest(order: Order) {
const deliveryDate = order.deliveredAt;
// Wait 2 days after delivery
await scheduleEmail({
sendAt: addDays(deliveryDate, 2),
to: order.buyer.email,
template: 'review-request',
data: {
orderId: order.id,
sellerName: order.seller.storeName,
items: order.items,
reviewUrl: `${baseUrl}/orders/${order.id}/review`
}
});
}
Review notification to seller
await sendEmail({
to: seller.email,
template: 'new-review',
data: {
rating: 5,
reviewText: 'Great product, fast shipping!',
orderDate: 'Dec 15, 2025',
// Don't include buyer name for privacy
responseUrl: `${baseUrl}/reviews/${review.id}/respond`
}
});
Email architecture for scale
Event-driven notifications
// Central event handler
async function handleMarketplaceEvent(event: MarketplaceEvent) {
const notifications = getNotificationsForEvent(event.type);
for (const notification of notifications) {
const recipients = await getRecipients(event, notification.recipientType);
for (const recipient of recipients) {
await queueEmail({
to: recipient.email,
template: notification.template,
data: await buildTemplateData(event, recipient, notification),
priority: notification.priority
});
}
}
}
// Event types
type MarketplaceEvent =
| { type: 'order.created'; order: Order }
| { type: 'order.shipped'; order: Order; tracking: TrackingInfo }
| { type: 'order.delivered'; order: Order }
| { type: 'dispute.opened'; dispute: Dispute }
| { type: 'payout.processed'; payout: Payout }
| { type: 'review.submitted'; review: Review };
Notification preferences
interface MarketplaceNotificationPrefs {
// Buyer preferences
orderUpdates: boolean; // Can't disable
shippingUpdates: boolean; // Can't disable
promotions: boolean; // Can disable
reviewReminders: boolean; // Can disable
// Seller preferences
newOrders: boolean; // Can't disable
payoutNotifications: boolean; // Can't disable
performanceReports: boolean; // Can disable
sellerTips: boolean; // Can disable
}
Best practices
- —Protect privacy - Never expose personal contact info between parties
- —Clear sender identity - Make it obvious emails are from the platform
- —Actionable content - Every email should have a clear next step
- —Consistent timing - Set expectations for when emails arrive
- —Audit trail - Log all communications for dispute resolution
- —Preference respect - Allow disabling non-essential notifications
Marketplace email is about building trust between strangers while protecting everyone's interests. Design your notifications to inform, protect, and facilitate smooth transactions.