Messaging, notifications, emails et push
Cette page décrit la chaîne messaging/notification vérifiée dans les modules Convex.
Sources principales :
convex/messaging/events.ts
convex/messaging/bus.ts
convex/messaging/notifications.ts
convex/email/outbox.ts
convex/email/dispatch.ts
convex/email/resend.ts
convex/social/recipient_groups.ts
Modèle d’événements
Le format d’entrée est MessagingEvent (validator dans events.ts) et les événements sont transformés en plan d’envoi (in-app + emails) par buildPlan dans bus.ts.
Événements observés (non exhaustif des payloads, mais exhaustif des kind listés dans le validator) :
auth.password_reset_requested
auth.email_verification_requested
feedback.submission_succeeded
feedback.submission_failed
ea.api.health_alerted
ea.potential_draw_review_required
user.unbanned
user.banned
user.reactivated
user.deleted
person_sanction.activated
person_sanction.ended
admin.bet_bulk_job.updated
admin.bet_bulk_job.partial_failure_alerted
admin.user_premium.updated
bet.status_changed
ea.score.confirmed
ea.score.unavailable
ea.score.ambiguous
ea.score.error
transfer.invitation.*
transfer.join_request.*
transfer.premium_break.executed
transfer.player.left_source_club
totw.published
season_awards.published
match.scheduling.*
match.dispute.reported
match.dispute.resolved
match.report.reported
match.postponement.*
match.result.auto_validated
match.result.diverged
match.result.admin_validated
match.result.contested
league.calendar.deleted
article.published
Zone non vérifiée
- Pour les événements en notation étoilée (
transfer.invitation.*, match.postponement.*, match.scheduling.*), seules les variantes codées dans events.ts et traitées dans bus.ts sont fiables ici.
Bus dispatch
dispatch (Mutation interne) :
- construit le plan via
buildPlan,
- applique
deliverInApp,
- résout les destinataires email,
- planifie l’action email
internal.email.dispatch.send via scheduler runAfter(0, ...).
dispatchAction (Action interne) :
- même logique de plan,
- écrit in-app via
recordInApp mutation,
- envoie emails via
internal.email.dispatch.send en action (pas de wrapper scheduler).
Le résumé de retour contient :
inApp.recipients|inserted|duplicates|skipped|notificationIds
email.requested|queued|failed
Fanout in-app et règles de sécurité
inAppTarget supportés : user, users, group (voir inAppTarget dans notifications.ts).
- Cibles de groupe via
resolveRecipientGroupMembers (social/recipient_groups.ts).
- Dedup :
- dédup par
(userId, dedupKey),
- pour
users, la key est suffixée par :<userId> pour éviter collision inter-destinataires.
match.scheduling.*, match.postponement.*, league.calendar.deleted ont des cibles explicites imposées (pas de groupe) via assertSafeInAppTarget.
assertTargetedInAppPlan exige pour ces événements :
- exactement une enveloppe in-app,
- cibles utilisateur explicites cohérentes avec les
recipientUserIds / managerUserIds,
- cap
MAX_MATCH_TARGETED_IN_APP_RECIPIENTS = 40.
Push
- push planifié par
schedulePushDeliveries en lots,
PUSH_DELIVERY_BATCH_SIZE = 200,
- chaque lot via
scheduler.runAfter(0, dispatchInAppNotifications).
Outbox / retry email
Queue
enqueueEmailBatch (email/outbox.ts) :
- déduplique par
dedupKey (index by_dedupKey),
- si existant + status !=
FAILED : skip,
- si existant +
FAILED : requeue (status PENDING, attemptCount reset),
- sinon insert
PENDING,
- déclenche
processDueEmailsAction avec limite par défaut DEFAULT_BATCH_SIZE = 20 (max MAX_BATCH_SIZE = 50).
Traitement batch
processDueEmailsAction :
claimDueEmails prend les due rows PENDING et passe en SENDING,
- envoie via
fetch("https://api.resend.com/emails"),
- en succès :
markEmailSent,
- en échec :
markEmailAttemptFailed,
- si taille du lot atteinte, auto replanifie le batch suivant.
Backoff/retry
- max tentatives:
MAX_RETRY_ATTEMPTS = 8,
- délai progressif
BASE_RETRY_DELAY_MS = 60_000 avec jitter + cap MAX_RETRY_DELAY_MS = 6h,
- status:
- réessai jusqu’au max :
PENDING + nextAttemptAt,
- au max atteint :
FAILED.
markEmailAttemptFailed planifie de nouveau processDueEmailsAction jusqu’à épuisement.
Envoi effectif
email/dispatch.ts expose send pour la couche unifiée.
sendEmailBatchCore (email/resend.ts) :
Promise.allSettled par destinataire,
- journalisation monitoring via
recordSendAttempts vers email_delivery_logs,
- retourne
{ requested, queued, failed }.
Legacy sendEmailBatch reste présent mais délègue au même core (sendEmailBatchCore) pour compatibilité admin.
Zone non vérifiée
- Le détail de l’intégration push (provider/service worker / permissions navigateur) n’est pas couvert dans les fichiers cités ici.
Last modified on June 24, 2026