Notifications

Référence technique du domaine Notifications — in-app, email transactionnel, SMS, templates, préférences utilisateur et queues persistantes.

Notifications

Périmètre : notifications in-app, email transactionnel, SMS transactionnel, templates configurables par organisation, préférences utilisateur, queues persistantes avec retry exponentiel, webhooks providers (Brevo, OVH SMS…).

Les écrans utilisateur vivent sous /account/notifications, /account/preferences/notifications et /workspace/admin/settings/notifications. Les routes API se trouvent sous /api/notifications et /api/account/preferences/notifications. Source de cette page : docs/15-domain-notifications.md.
Garde-fou production — un email n'est jamais envoyé sans EMAIL_PROVIDER configuré. La valeur par défaut est console : les emails sont simplement loggés en stdout, ce qui est sûr en développement et évite tout envoi accidentel.

Vocabulaire & entités

TermeEntité / CodeDéfinition
NotificationNotificationMessage à destination d'un utilisateur (in-app + canal externe optionnel).
CanalNotificationChannelIN_APP, EMAIL, SMS, PUSH (futur).
Typetype: stringCode stable identifiant le déclencheur (ex : lease.contract.signed).
TemplateEmailTemplate / SmsTemplateModèle de contenu paramétrable par organisation, référencé par code.
QueueEmailQueue / SmsQueueTable persistante gérée par jobs cron — statuts PENDING, SENDING, SENT, FAILED.
PréférenceNotificationPreferenceChoix utilisateur des canaux actifs pour un type donné.
Catalogueshared/notifications/catalog.tsSource de vérité de tous les types de notifications, leurs templates et audiences par défaut.

Le modèle complet est dans docs/04-data-model.md §12.

Catalogue des types

Chaque type a un code stable et définit : title template, body template, code template email/SMS, canaux par défaut, niveau d'importance (info / warning / critical) et audience cible (owner / manager / accountant…).

auth.*                    # invitation, reset mot de passe, MFA
identity.user.*
crm.opportunity.*
crm.action.*
sales.quote.*
sales.invoice.*
sales.payment.*
purchases.invoice.*
lease.affair.*
lease.contract.*
lease.lender_request.*
lease.cession.*
asset.maintenance.*
asset.location.*
admin.system.*
ai.workflow.*

Écrans & préférences

ÉcranRouteContenu
Centre de notifications/account/notificationsListe paginée (50 les plus récentes), filtres type / période / lu, marquer tout lu. Tap → marque lu + redirige vers linkUrl.
Icône topbar(topbar bell icon)Badge compteur non lus, preview liste rapide.
Préférences/account/preferences/notificationsTableau par domaine : choix canaux in-app / email / SMS par type. Toggle global "Pause X heures".
Templates admin/workspace/admin/settings/notificationsListe templates système + custom org, édition WYSIWYG markdown/HTML, preview données dummy, reset système.

Modèle de données (points clés)

  • Notification : toujours créée en canal IN_APP (canal incompressible) ; les autres canaux dépendent des préférences.
  • EmailQueue : colonnes status, attempts, scheduledAt, providerMessageId. Index composite (status, scheduledAt) pour le job de traitement.
  • NotificationPreference : clé (userId, type, channel), valeur booléenne activé/désactivé.
  • Index (userId, status, createdAt) sur Notification pour la liste du centre.

API

Toutes les routes /api/notifications n'exigent qu'une session authentifiée (pas de permission crm.* ou autre — chaque utilisateur ne voit que ses propres notifications). Les routes /api/workspace/admin/notifications/templates exigent admin.settings:read/update.

MéthodeRoutePermission
GET/api/notificationsauthentifié
POST/api/notifications/[id]/readauthentifié
POST/api/notifications/mark-all-readauthentifié
GET/api/notifications/unread-countauthentifié
GET/api/account/preferences/notificationsauthentifié
PATCH/api/account/preferences/notificationsauthentifié
GET/api/workspace/admin/notifications/templatesadmin.settings:read
POST/api/workspace/admin/notifications/templatesadmin.settings:update
POST/api/webhooks/brevoHMAC
POST/api/webhooks/smsHMAC
PATCH/api/account/preferences/notificationsAuth

Met à jour les préférences de notification de l'utilisateur courant pour un ou plusieurs types de notification.

Corps (JSON)

preferences
Array<{type, channel, enabled}> required
Tableau de préférences à modifier. Chaque entrée cible un couple type (code catalogue) et channel (EMAIL, SMS, IN_APP).

Requête

curl -s -X PATCH "$API/api/account/preferences/notifications" \
  -H "Content-Type: application/json" -H "Cookie: $SESSION" \
  -d '{"preferences":[{"type":"lease.contract.signed","channel":"SMS","enabled":false}]}'

Réponse

{ "updated": 1 }

Workflows

Émission d'une notification

Le service notifications.service.ts expose une fonction notify(opts) appelée par tous les autres services lors de mutations sensibles.

await notify({
  organizationId,
  userIds: ['usr_...'],         // ou résolution par rôle : { role: 'ACCOUNTANT' }
  type: 'lease.contract.signed',
  payload: { contractNumber: 'LLD-2026-000123', customerName: 'Lidl France' },
  linkUrl: `/workspace/lease/contracts/${contractId}`,
})

Flux interne :

  1. Résolution des utilisateurs cibles (userIds explicites ou lookup par rôle dans l'org).
  2. Pour chaque utilisateur :
    • Charge NotificationPreference pour ce type.
    • Crée Notification canal IN_APP (PENDING).
    • Canal EMAIL activé → enqueue EmailQueue avec template rendu.
    • Canal SMS activé → enqueue SmsQueue.

Job d'envoi email (send-pending-emails)

Cadence : toutes les 1 minute — traite 50 EmailQueue PENDING dont scheduledAt <= now.

Pour chaque entrée :
  1. Marque SENDING
  2. Appelle provider (Brevo POST /smtp/email)
  3. Succès → SENT + stocke providerMessageId
  4. Échec + attempts < maxAttempts → reschedule (backoff : 1 min / 5 min / 15 min / 1h / 6h)
  5. Échec + attempts >= maxAttempts → FAILED + alerte admin

Même logique pour le job SMS.

Providers email

Brevo (défaut)

BREVO_API_KEYhttps://api.brevo.com/v3/smtp/email. Webhooks delivery / open / click via POST /api/webhooks/brevo (signature HMAC).

Scaleway TEM (alternative)

Email transactionnel via SMTP Scaleway. Détails dans docs/18-integrations.md.

console (dev)

Valeur par défaut de EMAIL_PROVIDER. Logs en stdout uniquement — aucun envoi réel. Obligatoire en dev pour éviter tout trafic accidentel.

Providers SMS

OVH SMS, Scaleway SMS ou Twilio selon variable d'environnement. Webhooks entrants sur POST /api/webhooks/sms (HMAC) pour les événements delivered / failed.

Tracking livraison (webhooks entrants)

  • POST /api/webhooks/brevo : événements delivered, opened, clicked, bounce, spam → mise à jour Notification.status.
  • POST /api/webhooks/sms : événements delivered, failed.
  • Signature HMAC vérifiée ; requête invalide → 401.

Règles métier

  • IN_APP toujours envoyée (sauf préférence explicite OFF par l'utilisateur).
  • EMAIL et SMS soumis aux préférences utilisateur par type.
  • Hard bounce : désactive le canal EMAIL pour ce user définitivement + notifie l'admin.
  • Spam complaint : désactive le canal EMAIL définitivement.
  • Throttling SMS : max 1 SMS / 5 min par destinataire / type (le 2ᵉ envoi rapide est reschedulé).
  • RGPD : opt-out clair en footer pour les emails non strictement transactionnels.
  • Templates système : non éditables directement — surchargables par organisation, mais le template système lui-même est immuable.
  • Audit log sur toute modification de template (admin.settings:update).
  • Performance : queue traite jusqu'à 50 emails / minute (configurable) ; batch Brevo jusqu'à 1 000 destinataires pour un même payload lors de pics.