L’email B2B diffère fondamentalement de l’email grand public. Les clients d’entreprise attendent de la fiabilité, des informations détaillées et une intégration à leurs workflows. Voici comment concevoir des systèmes de notification B2B.
Caractéristiques de l’email B2B
L’email pour l’entreprise doit gérer :
- —Plusieurs destinataires par organisation
- —Notifications basées sur les rôles
- —Exigences d’audit et de conformité
- —Intégration avec les outils d’entreprise
- —Attentes de délivrabilité plus élevées
Notifications au niveau de l’organisation
Alertes d’équipe
interface TeamAlert {
organization: {
id: string;
name: string;
};
alert: {
type: 'security' | 'billing' | 'usage' | 'system';
severity: 'info' | 'warning' | 'critical';
title: string;
details: string;
};
recipients: TeamMember[];
}
async function sendTeamAlert(alert: TeamAlert) {
// Filter recipients by role and preferences
const recipients = alert.recipients.filter(member =>
shouldReceiveAlert(member, alert.alert.type, alert.alert.severity)
);
for (const recipient of recipients) {
await sendEmail({
to: recipient.email,
subject: `[${alert.alert.severity.toUpperCase()}] ${alert.alert.title}`,
template: 'team-alert',
data: {
organization: alert.organization,
alert: alert.alert,
recipient,
actionUrl: getAlertActionUrl(alert)
},
headers: {
'X-Priority': alert.alert.severity === 'critical' ? '1' : '3'
}
});
}
}
Notifications administrateur
interface AdminNotification {
type: 'user_added' | 'user_removed' | 'role_changed' | 'settings_changed';
actor: { name: string; email: string };
target?: { name: string; email: string };
details: Record<string, any>;
timestamp: Date;
}
// Notify all admins of security-relevant changes
async function notifyAdmins(org: Organization, notification: AdminNotification) {
const admins = await getOrgAdmins(org.id);
for (const admin of admins) {
await sendEmail({
to: admin.email,
subject: `[${org.name}] ${formatNotificationType(notification.type)}`,
template: 'admin-notification',
data: {
org,
notification,
auditLogUrl: `${baseUrl}/admin/audit-log`
}
});
}
}
Notifications basées sur les rôles
Envoi tenant compte des permissions
interface RoleBasedEmail {
organization: Organization;
emailType: string;
data: Record<string, any>;
}
async function sendRoleBasedEmail(config: RoleBasedEmail) {
const roleConfig = {
'billing.invoice': ['owner', 'billing_admin'],
'security.alert': ['owner', 'security_admin', 'admin'],
'usage.warning': ['owner', 'admin'],
'user.joined': ['owner', 'admin', 'hr_admin'],
'feature.announcement': ['owner', 'admin', 'user']
};
const allowedRoles = roleConfig[config.emailType] || ['owner'];
const recipients = await getOrgMembersByRoles(config.organization.id, allowedRoles);
for (const recipient of recipients) {
await sendEmail({
to: recipient.email,
template: config.emailType.replace('.', '-'),
data: {
...config.data,
recipient,
organization: config.organization
}
});
}
}
Modèles d’escalade
interface EscalationConfig {
initialRecipients: string[]; // roles
escalateAfter: number; // minutes
escalateTo: string[]; // roles
maxEscalations: number;
}
async function sendWithEscalation(
org: Organization,
alert: Alert,
config: EscalationConfig
) {
// Send initial notification
await sendToRoles(org, alert, config.initialRecipients);
// Schedule escalation check
await scheduleJob({
type: 'check_escalation',
runAt: addMinutes(new Date(), config.escalateAfter),
data: {
alertId: alert.id,
orgId: org.id,
escalationLevel: 1,
config
}
});
}
async function checkEscalation(job: EscalationJob) {
const alert = await getAlert(job.data.alertId);
if (alert.acknowledged) return;
if (job.data.escalationLevel < job.data.config.maxEscalations) {
// Escalate to next level
await sendToRoles(
await getOrg(job.data.orgId),
alert,
job.data.config.escalateTo
);
// Schedule next escalation
await scheduleJob({
...job,
runAt: addMinutes(new Date(), job.data.config.escalateAfter),
data: {
...job.data,
escalationLevel: job.data.escalationLevel + 1
}
});
}
}
Emails de facturation d’entreprise
Notifications de facture
interface InvoiceEmail {
organization: Organization;
invoice: {
id: string;
number: string;
amount: number;
currency: string;
dueDate: Date;
lineItems: LineItem[];
pdfUrl: string;
};
}
await sendEmail({
to: billingContact.email,
subject: `Invoice #${invoice.number} for ${org.name}`,
template: 'invoice',
data: {
organization: org,
invoice,
paymentUrl: `${baseUrl}/billing/pay/${invoice.id}`,
supportEmail: '[email protected]'
},
attachments: [
{
filename: `invoice-${invoice.number}.pdf`,
path: invoice.pdfUrl
}
]
});
Alertes d’usage
const usageAlertThresholds = [
{ percent: 75, template: 'usage-warning-75' },
{ percent: 90, template: 'usage-warning-90' },
{ percent: 100, template: 'usage-limit-reached' }
];
async function checkUsageAlerts(org: Organization) {
const usage = await getOrgUsage(org.id);
const limit = org.plan.limits;
const percent = (usage.current / limit.max) * 100;
for (const threshold of usageAlertThresholds) {
if (percent >= threshold.percent && !await hasAlertedThreshold(org.id, threshold.percent)) {
await sendRoleBasedEmail({
organization: org,
emailType: 'usage.warning',
data: {
currentUsage: usage.current,
limit: limit.max,
percent,
upgradeUrl: `${baseUrl}/billing/upgrade`
}
});
await markThresholdAlerted(org.id, threshold.percent);
}
}
}
Conformité et audit
Emails de piste d’audit
// Send audit summary to compliance team
async function sendAuditDigest(org: Organization) {
const events = await getAuditEvents(org.id, {
since: subDays(new Date(), 7),
types: ['security', 'access', 'data']
});
const complianceContacts = await getComplianceContacts(org.id);
for (const contact of complianceContacts) {
await sendEmail({
to: contact.email,
subject: `Weekly audit digest for ${org.name}`,
template: 'audit-digest',
data: {
organization: org,
period: 'Last 7 days',
summary: {
totalEvents: events.length,
byType: groupBy(events, 'type'),
flaggedEvents: events.filter(e => e.flagged)
},
fullReportUrl: `${baseUrl}/admin/audit-log`
}
});
}
}
Notifications d’export de données
// GDPR data export ready
await sendEmail({
to: requester.email,
subject: `Data export ready for ${org.name}`,
template: 'data-export-ready',
data: {
organization: org,
export: {
requestedAt: exportRequest.createdAt,
expiresAt: addDays(new Date(), 7),
downloadUrl: exportRequest.downloadUrl,
format: 'JSON'
},
securityNote: 'This link expires in 7 days. Download contains sensitive data.'
}
});
Notifications d’intégration
Alertes d’échec de webhook
await sendEmail({
to: techContact.email,
subject: `[Action Required] Webhook failures for ${org.name}`,
template: 'webhook-failures',
data: {
organization: org,
webhook: {
url: webhook.url,
failureCount: webhook.consecutiveFailures,
lastError: webhook.lastError,
lastAttempt: webhook.lastAttemptAt
},
action: webhook.consecutiveFailures >= 10
? 'Webhook has been disabled'
: 'Will retry automatically',
settingsUrl: `${baseUrl}/settings/webhooks`
}
});
Préférences de notification d’entreprise
interface EnterpriseNotificationPrefs {
// Organization-level defaults
orgDefaults: {
securityAlerts: 'all_admins' | 'security_team' | 'owner_only';
billingNotifications: 'billing_contact' | 'all_admins';
usageAlerts: boolean;
weeklyDigest: boolean;
};
// Per-user overrides
userOverrides: {
[userId: string]: {
emailEnabled: boolean;
digestFrequency: 'daily' | 'weekly' | 'never';
alertTypes: string[];
};
};
}
Meilleures pratiques
- —Diffusion basée sur les rôles - Envoyez aux bonnes personnes selon leurs responsabilités
- —Niveaux de gravité clairs - Rendez l’urgence évidente dans les objets
- —Contenu actionnable - Incluez des liens directs pour résoudre les problèmes
- —Auditez tout - Journalisez toutes les notifications pour la conformité
- —Parcours d’escalade - Assurez-vous que les alertes critiques atteignent quelqu’un
- —Respectez les préférences - Offrez un contrôle granulaire des notifications
L’email B2B repose sur la fiabilité et la confiance. Vos notifications doivent aider les organisations à fonctionner sans accroc tout en maintenant des standards de sécurité et de conformité.