emailr_
All articles
usecase·9 min

Email for marketplaces: Multi-party notifications

marketplacenotificationspatterns

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

  1. Protect privacy - Never expose personal contact info between parties
  2. Clear sender identity - Make it obvious emails are from the platform
  3. Actionable content - Every email should have a clear next step
  4. Consistent timing - Set expectations for when emails arrive
  5. Audit trail - Log all communications for dispute resolution
  6. 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.

e_

Written by the emailr team

Building email infrastructure for developers

Ready to start sending?

Get your API key and send your first email in under 5 minutes. No credit card required.