Rappel quotidien des tâches Lemlist en 1:1 Teams avec n8n¶
Chez Dolist, l'équipe commerciale travaille ses séquences de prospection dans Lemlist. Chaque séquence génère des tâches manuelles (un appel, un message LinkedIn, une visite de profil) que Lemlist affiche très bien... dans Lemlist. Le problème, c'est que personne ne vit dans Lemlist toute la journée. Voici comment un petit workflow n8n a suffi à faire atterrir ces tâches là où l'attention se trouve déjà : Teams.
Le problème : des tâches qui existent mais que personne ne regarde¶
Chaque séquence de prospection génère des tâches manuelles : un appel à passer, un message LinkedIn à envoyer, une visite de profil à faire avant de relancer. Lemlist affiche très bien ces tâches... dans Lemlist. Le problème, c'est que personne ne vit dans Lemlist toute la journée. L'outil de travail réel des commerciaux, c'est Teams : c'est là qu'ils reçoivent leurs notifications, qu'ils échangent avec leur manager, qu'ils commencent leur journée.
Résultat observé sur le terrain : des tâches qui s'accumulent, débordent sur plusieurs jours, et finissent identifiées seulement lors d'un point hebdomadaire, bien après leur date d'échéance. Ce n'est pas un problème de discipline individuelle, c'est un problème de canal : l'information existe, mais elle n'est pas là où l'attention se trouve déjà.
Pourquoi pas simplement le rappel natif Lemlist¶
Les notifications email natives de Lemlist ne couvrent pas les tâches à traiter : elles portent sur la santé des campagnes (déconnexion d'une boîte mail, taux de bounce élevé, fin de campagne), pas sur les rappels d'action. Le seul canal natif réellement lié aux tâches est une intégration Slack (via webhook), qui a trois limites concrètes :
- Le mauvais outil. L'équipe travaille sur Teams, pas Slack. Brancher cette intégration aurait ajouté un outil supplémentaire à surveiller plutôt que de centraliser l'information là où les commerciaux sont déjà.
- Une couverture partielle. Cette intégration ne notifie que les tâches créées automatiquement par une activité du lead (réponse par email/LinkedIn, clic sur un lien), pas les tâches "Manual task" ou "Call" posées directement dans une séquence. Une bonne partie des tâches réelles de l'équipe (appels, visites de profil LinkedIn) ne déclencherait donc jamais de notification par cette voie.
- Le manque de synthèse. Même pour les tâches couvertes, la notification part tâche par tâche, au fil de l'eau. L'objectif ici : un point d'entrée unique le matin, une liste consolidée de tout ce qui est dû aujourd'hui et demain, groupée par personne, avec un minimum de mise en forme pour prioriser en un coup d'œil.
Ce qu'on voulait construire¶
Un workflow simple, qui tourne seul chaque matin ouvré et qui :
- Interroge l'API Lemlist pour récupérer les tâches à échéance aujourd'hui ou demain.
- Filtre le bruit (tâches déjà traitées, tâches système type détection d'absence).
- Regroupe les tâches par commercial et construit un message lisible, avec une icône par type d'action.
- Envoie ce message dans la conversation Teams 1:1 du bon commercial, jamais dans un canal partagé.
Pas de dashboard, pas d'interface : juste un message qui arrive au bon endroit, au bon moment, avec le bon contenu.
Architecture du workflow¶
flowchart TD
A["Schedule Trigger\n0 7 * * 1-5 (jours ouvres, 7h Paris)"] --> B["HTTP Request\nGET api.lemlist.com/api/tasks\nfiltre dueDate = aujourd'hui -> demain"]
B --> C["Code : grouper par commercial\nfiltre + regroupe + formate le message"]
C --> D{"Switch sur userId Lemlist"}
D --> E["Teams 1:1 Commercial A"]
D --> F["Teams 1:1 Commercial B"]
D --> G["Teams 1:1 Commercial C"]
D --> H["Teams 1:1 Commercial D"]
Cinq briques, aucune branche conditionnelle exotique : un déclencheur, un appel API, un nœud de code pour la logique métier, un routeur, et quatre sorties. La complexité n'est pas dans le nombre de nœuds, elle est concentrée dans le nœud de code qui fait tout le travail de filtrage et de mise en forme.
Construction, nœud par nœud¶
1. Le déclencheur : jours ouvrés, 7h du matin¶
Un Schedule Trigger classique, avec une expression cron plutôt qu'un intervalle simple, pour exclure le week-end sans nœud supplémentaire :
Fuseau horaire du workflow réglé sur Europe/Paris. Le rappel arrive donc avant la prise de poste, jamais le samedi ni le dimanche.
2. Récupérer les tâches dues via l'API Lemlist¶
Un nœud HTTP Request interroge directement GET https://api.lemlist.com/api/tasks avec un filtre passé en query parameter :
[{"id": "dueDate", "from": "{{ $now.format('yyyy-MM-dd') }}", "to": "{{ $now.plus({ days: 1 }).format('yyyy-MM-dd') }}"}]
Deux choix à noter ici. D'abord, la fenêtre couvre aujourd'hui et demain, pas seulement aujourd'hui : l'idée est de laisser un commercial anticiper sa journée du lendemain dès la veille au soir... sauf que le workflow tourne le matin, donc en pratique cette fenêtre sert surtout à ne rater aucune tâche à cheval sur le changement de date (fuseau horaire, tâche créée tard). Ensuite, le filtrage fin (statut, type de tâche à exclure) n'est volontairement pas fait ici : l'API Lemlist expose un filtre par date, pas par statut ni par contenu, donc toute la logique métier est repoussée dans le nœud suivant.
3. Le nœud de code : filtrer, grouper, formater¶
C'est le cœur du workflow. Un nœud Code (JavaScript) qui fait quatre choses en séquence.
a) Normaliser la réponse API. La forme exacte de la réponse Lemlist (tableau brut ou objet enveloppant results/tasks/data/items) n'est pas garantie selon l'endpoint ou la version d'API. Une petite fonction toArray() sécurise ce point plutôt que de supposer une forme fixe :
function toArray(x) {
if (Array.isArray(x)) return x;
if (x && typeof x === 'object') {
for (const key of ['results', 'tasks', 'data', 'items']) {
if (Array.isArray(x[key])) return x[key];
}
}
return [];
}
b) Filtrer le bruit. Trois conditions cumulées : le statut doit être vide ou pending (donc pas déjà traité), le titre ne doit pas être "Out of office detected" (une tâche système que Lemlist génère automatiquement, sans valeur d'action pour le commercial), et la date d'échéance doit tomber sur aujourd'hui ou demain.
c) Grouper par commercial. Chaque tâche Lemlist porte un userId. Un simple objet accumulateur regroupe les tâches par propriétaire :
const byUser = {};
for (const task of tasks) {
const uid = task.userId || 'non_assigne';
if (!byUser[uid]) byUser[uid] = [];
byUser[uid].push(task);
}
d) Construire le message. Chaque type de tâche Lemlist reçoit une icône dédiée (📝 manuel, 📞 appel, 🔗 invitation LinkedIn, 💬 message LinkedIn ou WhatsApp, 👀 visite de profil, ✉️ email, 📱 SMS), et le message final liste les dix premières tâches avec la mention "aujourd'hui" ou "demain", puis résume le surplus (...et N autre(s)) si la personne en a plus de dix.
4. Router vers le bon commercial¶
Un nœud Switch avec quatre règles, une par userId Lemlist, chacune connectée à sa propre sortie. C'est une correspondance en dur entre un identifiant Lemlist et une branche du workflow, pas une résolution dynamique.
5. Envoyer le message en 1:1 Teams¶
Quatre nœuds Microsoft Teams (ressource chatMessage), chacun ciblant l'ID de conversation 1:1 exact entre le commercial et le compte technique qui exécute le workflow. Le message envoyé est celui construit à l'étape 3, injecté via une expression n8n qui référence le nœud de code.
Le mockup du canvas (et pourquoi ce n'est pas une vraie capture)¶
L'idée était d'illustrer cet article avec une vraie capture d'écran du canvas n8n, données personnelles floutées. En pratique, l'environnement depuis lequel cet article a été rédigé bloquait le chargement de l'interface n8n (assets réseau filtrés côté navigateur) : pas de vraie capture disponible cette fois. Plutôt que de bricoler un faux visuel, voici une reconstitution fidèle de la disposition réelle des nœuds sur le canvas, avec les noms des commerciaux anonymisés :
flowchart LR
subgraph Canvas["Canvas n8n - vue d'ensemble"]
direction LR
T["Tous les matins 7h\n(jours ouvres)"] --> R["Lemlist -\nTaches dues"]
R --> G["Grouper par\ncommercial"]
G --> S["Switch -\nQuel commercial"]
S --> N1["Teams - Envoyer\na Commercial A"]
S --> N2["Teams - Envoyer\na Commercial B"]
S --> N3["Teams - Envoyer\na Commercial C"]
S --> N4["Teams - Envoyer\na Commercial D"]
end
Les noms des nœuds ci-dessus sont les noms réels donnés dans le workflow (Tous les matins 7h (jours ouvres), Lemlist - Taches dues, Grouper par commercial, Switch - Quel commercial) ; seuls les noms des quatre commerciaux en sortie du Switch ont été remplacés.
Ce qui fonctionne bien¶
- Le bon canal. Un message Teams en 1:1 a un taux d'attention largement supérieur à une notification email, sans avoir eu besoin de développer une intégration complexe.
- Une fenêtre de tolérance courte. Regrouper uniquement "aujourd'hui + demain" évite l'effet liste interminable qui décourage de lire, tout en laissant un peu de marge d'anticipation.
- La normalisation défensive de la réponse API. Le
toArray()a évité au moins une classe de panne silencieuse si Lemlist change un jour la forme de sa réponse. - Le filtrage du bruit système. Exclure
"Out of office detected"a été ajouté après avoir vu ce type de tâche polluer un premier message de test.
Limites et pièges identifiés¶
Les commerciaux non mappés tombent dans le vide
Le nœud de code assigne 'non_assigne' comme clé quand une tâche n'a pas de userId, mais le Switch en aval ne connaît que quatre userId précis. Toute tâche appartenant à un cinquième commercial, ou à un compte sans userId, ne matche aucune des quatre règles et n'a aucune sortie configurée pour ce cas : le message est silencieusement perdu, sans erreur visible dans l'historique d'exécution. Ajouter un nouveau commercial suppose de modifier le Switch et d'ajouter un nœud Teams, pas juste une ligne de configuration.
Pas de lien direct vers la tâche. Le message liste les tâches par titre et type, mais ne contient pas de lien cliquable vers la fiche Lemlist correspondante. Le commercial doit rouvrir Lemlist manuellement pour agir, ce qui réduit une partie du gain de friction visé au départ.
Une petite incohérence de style entre les branches. Trois des quatre nœuds Teams référencent explicitement le nœud source ($('Grouper par commercial').item.json.message), tandis que le nœud "Commercial B" utilise la référence implicite à l'item courant ($json.message) et fixe en plus contentType: html alors que les trois autres n'en précisent pas. Le résultat final est identique à l'exécution (chaque branche du Switch ne reçoit qu'un seul item), mais c'est le genre de divergence qui devient un vrai bug si le workflow est un jour modifié pour fusionner ou dupliquer des branches.
Aucune déduplication entre deux exécutions. Une tâche vue "pour demain" un jour sera revue "pour aujourd'hui" le lendemain matin : c'est voulu (rappel progressif), mais ça veut dire qu'une tâche non traitée à temps ne génère pas de relance renforcée, elle disparaît simplement de la fenêtre une fois sa date dépassée.
Ce qui serait à refaire autrement¶
Remplacer le Switch statique par une résolution dynamique du canal Teams à partir d'une table de correspondance (Data Table n8n ou petite base externe), avec une branche de repli explicite pour les userId non reconnus, plutôt qu'un silence total. Ajouter aussi un lien direct vers chaque tâche dans le message, si l'URL de la tâche est exposée par l'API Lemlist (à vérifier), pour supprimer l'étape de recherche manuelle. Enfin, harmoniser les quatre nœuds Teams pour qu'ils utilisent tous la même syntaxe d'expression, question d'hygiène de maintenance plus que de correction fonctionnelle.
FAQ¶
Pourquoi Teams et pas Slack ? Parce que c'est l'outil de messagerie interne réellement utilisé par l'équipe commerciale. La logique se transposerait telle quelle sur Slack en remplaçant les quatre nœuds Teams par des nœuds Slack ciblant des DM.
Pourquoi grouper "aujourd'hui + demain" plutôt que juste "aujourd'hui" ? Pour absorber les petits décalages de fuseau horaire ou d'heure de création de tâche, et donner un peu de visibilité sur le lendemain sans changer l'heure d'exécution du workflow.
Le workflow gère-t-il les tâches en retard (échéance passée) ?
Non. Le filtre ne couvre que la fenêtre [aujourd'hui, demain]. Une tâche dont l'échéance est passée sans avoir été traitée sort simplement du périmètre du rappel.
Que se passe-t-il si un nouveau commercial rejoint l'équipe ?
Il faut éditer manuellement le nœud Switch pour ajouter son userId Lemlist, et créer un nouveau nœud Teams pointant vers sa conversation 1:1. Ce n'est pas automatique, voir la section limites.
Conclusion¶
Ce workflow ne fait rien de spectaculaire : il déplace une information qui existait déjà d'un outil où elle était ignorée vers un canal où elle est vue. C'est souvent là que se trouve la vraie valeur d'un automatisme en RevOps, pas dans la sophistication technique mais dans le choix du bon canal au bon moment. Les limites identifiées ici, en particulier le mapping figé entre commercial et conversation Teams, sont typiques d'un premier jet volontairement simple : suffisant pour une équipe de quatre personnes, à revoir avant tout passage à l'échelle.
Le même pattern de routage "un Switch par destinataire, un nœud Teams par branche" a ensuite été repris pour un besoin différent : voir Générer des posts LinkedIn avec l'IA via n8n.