| 1 | Webhook Déclencheur · n8n-nodes-base.webhook · v2.1 | Reçoit la demande de rapport (depuis le formulaire ou un autre outil).- Chemin
POST /webhook/kpi/report, responseMode = responseNode : la réponse est envoyée plus loin par un node Respond.- Sécurité
- Pas d'authentification pour l'instant (prévue en phase suivante dans le contrat API).
|
| 2 | Init params Code JS · n8n-nodes-base.code · v2 | Vérifie la demande et calcule les périodes. Si period_months est donné, il calcule les N derniers mois pleins et la même fenêtre un an plus tôt.- Contrôles
- Champs requis, format
YYYY-MM-DD, début avant fin, pas de date future, période N de 730 jours maximum, N et N-1 sans chevauchement, period_months dans 3/6/12. - Écrit
validation{ok,http,body}, company_name_norm (sans accents ni ponctuation), period_n, period_n1, date_rapport, year_n, year_n1.- Choix
- Ne lève jamais d'erreur : il renvoie un objet validation, aiguillé ensuite par IF. Une seule erreur est renvoyée (la première).
|
| 3 | IF Validation OK? Condition · n8n-nodes-base.if · v2.2 | Aiguillage : demande valide, on continue. Sinon, on répond tout de suite l'erreur 400.- Condition
$json.validation.ok est vrai.- Sorties
- vrai : les deux lectures de la sheet. faux :
Respond Error Early.
|
| 4 | Read EXPORT Google Sheets · n8n-nodes-base.googleSheets · v4.5 | Lit tout l'onglet « Feuille 1 » d'EXPORT_SAYRANK : une ligne par client avec ses IDs Google, ses emails de compte et ses notes QualiAvis.- Credential
- Google Sheets account - saycomtech@gmail.com
- Cible
- Document EXPORT_SAYRANK, onglet Feuille 1 (
gid=0). - Robustesse
- 3 tentatives espacées de 2 s (des erreurs 503 ont été vues). Pas de tolérance au-delà : sans la sheet, pas de client.
|
| 5 | Read Sayrank sheet Google Sheets · n8n-nodes-base.googleSheets · v4.5 | Lit l'onglet « Liste entreprise Sayrank », qui sert de secours pour trouver le codecrm du client.- Credential
- Google Sheets account - saycomtech@gmail.com
- Cible
- Même document, onglet en mode
id : 1059020634. Le format gid= ne marche pas pour un onglet secondaire. - Robustesse
- 3 tentatives espacées de 2 s.
|
| 6 | Lookup unique Code JS · n8n-nodes-base.code · v2 | Retrouve le client dans la sheet à partir du nom tapé, puis rassemble tout ce qu'il faut savoir sur lui : comptes Google, IDs, codecrm, site, QualiAvis, activité.- Recherche
- Nom normalisé comparé aux colonnes
Nom, nom_client_ga4, nom_client_gbp : égalité d'abord, puis inclusion. Garde-fou : sous 6 caractères, seule l'égalité compte. - codecrm
- Colonne CRM de la Feuille 1 en priorité (plusieurs intitulés acceptés), sinon jointure par
ID avec la Liste entreprise Sayrank. - Écrit
ga4_property_id, ga4_email, gbp_location_id, gbp_email, gbp_email_local, codecrm, site_ga4, domain, qualiavis_note_raw, qualiavis_count_raw, activite_concurrents, sheet_id.- Erreur
- Client introuvable : validation 404
company_not_found.
|
| 7 | IF Lookup OK? Condition · n8n-nodes-base.if · v2.2 | Client trouvé, on continue. Sinon, réponse 404.- Condition
$json.validation.ok est vrai.
|
| 8 | Validate integrations Code JS · n8n-nodes-base.code · v2 | Vérifie que le client est bien branché : un compte GA4, une fiche Google et un codecrm SayRank. Sinon, réponse 422.- Contrôles
ga4_email + ga4_property_id, gbp_email + gbp_location_id, codecrm.- Erreurs
ga4_not_connected, gbp_not_connected, codecrm_not_resolved. Seule la première manquante est nommée, la liste complète est dans details.missing_integrations.
|
| 9 | IF Integrations OK? Condition · n8n-nodes-base.if · v2.2 | Tout est branché : on lance les 6 collectes en même temps. Sinon, réponse 422.- Sortie vrai
- 6 branches parallèles : WF1, WF2, WF3, WF4, fiche GBP, e-mails.
|
| 10 | Respond Error Early Réponse · n8n-nodes-base.respondToWebhook · v1.5 | Répond l'erreur au demandeur, avec le bon code HTTP et un message en français.- Réponse
- Corps =
validation.body en JSON, code HTTP = validation.http (dynamique), Content-Type: application/json. - À savoir
- Un seul node pour les 3 sorties « faux » (400, 404, 422).
|
| 11 | Call WF1 GA4 Requête HTTP · n8n-nodes-base.httpRequest · v4.2 | Demande à WF1 les chiffres de fréquentation du site (GA4).- Appel
POST /webhook/kpi/ga4-period avec company_name, period_n, period_n1. Timeout 60 s.- Robustesse
onError = continueRegularOutput + alwaysOutputData : en cas de panne, le rapport sort sans GA4.
|
| 12 | Call WF2 GBP Requête HTTP · n8n-nodes-base.httpRequest · v4.2 | Demande à WF2 les performances de la fiche Google (apparitions, appels, clics).- Appel
POST /webhook/kpi/gbp-perf. Timeout 60 s.- Robustesse
- Tolérant aux pannes (comme les 5 autres collectes).
|
| 13 | Call WF3 reviews Requête HTTP · n8n-nodes-base.httpRequest · v4.2 | Demande à WF3 la note et le nombre d'avis Google.- Appel
POST /webhook/kpi/gbp-reviews avec company_name seul (les avis ne dépendent pas de la période). Timeout 60 s.- Robustesse
- Tolérant aux pannes.
|
| 14 | Call WF4 Sayrank Requête HTTP · n8n-nodes-base.httpRequest · v4.2 | Demande à WF4 les positions des mots-clés suivis dans SayRank.- Appel
POST /webhook/kpi/sayrank-positions avec codecrm et company_name. Timeout 60 s.- Robustesse
- Tolérant aux pannes.
|
| 15 | Prepare GBP loc req Code JS · n8n-nodes-base.code · v2 | Prépare la question à poser à Google sur la fiche du client : adresse, catégorie principale, nom affiché.- Écrit
url = API Business Information v1/locations/<ID GBP> avec readMask=storefrontAddress,categories.primaryCategory.displayName,title, method = GET, gbp_email_local.
|
| 16 | Emails count Base Postgres · n8n-nodes-base.postgres · v2.6 | Compte les demandes reçues par les formulaires du site du client sur la période, sans le spam.- Credential
- Forms_submission (Postgres)
- Requête
COUNT(*) sur la table submissions : URL comparée sans protocole, sans www. ni slash final, created_at dans la période N, status IS DISTINCT FROM 'spam'.- Clé site
site_ga4, sinon domain s'il contient un point, sinon - (aucun résultat).- Piège
IS DISTINCT FROM et pas != : sinon les soumissions au statut NULL (non modérées, donc les plus récentes) disparaissent.
|
| 17 | Call GBP location Appel sub-workflow · n8n-nodes-base.executeWorkflow · v1.2 | Pose cette question via sub-gbp-call, qui choisit le bon compte Google. Donne la ville et l'activité du rapport.- Cible
- sub-gbp-call
9ZtvIxsyFt1hY0JT. - Robustesse
- Tolérant aux pannes : ville et activité passent alors à
—.
|
| 18 | Merge KPIs Fusion · n8n-nodes-base.merge · v3 | Attend que les 6 collectes aient répondu et les met ensemble.- Entrées
- 0 WF1, 1 WF2, 2 WF3, 3 WF4, 4 fiche GBP, 5 e-mails.
- À savoir
Compute derived relit ce node par son nom et reconnaît chaque source à la forme de sa réponse, pas à son numéro d'entrée.
|
| 19 | Prep concurrents Code JS · n8n-nodes-base.code · v2 | Prépare la recherche des concurrents sur Google Maps, ou ressert le résultat déjà en cache.- Requête
- Colonne « activité » d'EXPORT_SAYRANK (remplie par Saycom), sinon catégorie principale de la fiche Google. La ville est ajoutée seulement si elle n'y est pas déjà.
- Cache
- 30 jours dans la staticData du workflow, par
gbp_location_id (ou codecrm). Entrées versionnées (v = '3-5') et purgées à chaque passage. - Écrit
skip (vrai si cache ou pas de requête possible), q, self (nom du client).
|
| 20 | IF concurrents ? Condition · n8n-nodes-base.if · v2.2 | Faut-il vraiment interroger SerpApi ? Oui si rien en cache. Sinon on passe directement au calcul.- Condition
$json.skip est faux.- Sorties
- vrai : Call SerpApi. faux : Compute derived, avec le cache ou une liste vide.
|
| 21 | Call SerpApi Requête HTTP · n8n-nodes-base.httpRequest · v4.2 | Fait la recherche Google Maps « métier + ville » et récupère la liste des entreprises dans l'ordre de Google.- Credential
- SerpApi (authentification par paramètre de requête).
- Appel
GET https://serpapi.com/search.json, engine=google_maps, type=search, hl=fr, gl=fr.- Robustesse
- 2 tentatives espacées de 2 s, tolérant aux pannes : quota épuisé ou API absente, le bloc concurrents est masqué.
- Quota
- Compte en offre gratuite : 250 recherches par mois.
|
| 22 | Parse concurrents Code JS · n8n-nodes-base.code · v2 | Garde les 5 premiers résultats tels que Google les classe, repère le client dans la liste, et met le tout en cache.- Logique
- Aucun tri ni filtre : la liste doit être celle que Soun retrouve en tapant la recherche. Le client est reconnu par son nom normalisé (le data_cid de SerpApi ne correspond pas à l'ID GBP).
- Écrit
competitors[{RANK, NOM, NOTE, AVIS, YOU}], me_rank, q. Écrit aussi le cache.
|
| 23 | Compute derived Code JS · n8n-nodes-base.code · v2 | Le cœur du calcul : transforme les réponses brutes en toutes les cases du rapport (chiffres formatés en français, évolutions, flèches, textes selon la période, blocs à afficher ou masquer).- Lit
$('Merge KPIs').all(), $('Lookup unique'), et les concurrents en entrée directe.- Écrit
{data, _raw} : data contient les variables du template (SESSIONS_N, CALLS_N, NOTE_GOOGLE, KEYWORDS, CONCURRENTS, flags HAS_…).- Règles
- Durée de période → TYPE_RAPPORT (Mensuel, Trimestriel, Semestriel, Annuel). Si le client n'est pas dans le top 5 des concurrents, sa ligne est ajoutée à la fin, avec « 20+ » s'il est absent des 20 premiers. QualiAvis affiché seulement si note et nombre d'avis sont supérieurs à 0. Ads figé à HAS_ADS = false.
|
| 24 | Call WF6 render Requête HTTP · n8n-nodes-base.httpRequest · v4.2 | Envoie toutes les cases à WF6, qui les place dans le template et renvoie la page HTML.- Appel
POST /webhook/kpi/render avec {data}, réponse en texte, timeout 60 s.- À savoir
- Seul appel bloquant : sans HTML, il n'y a rien à répondre.
|
| 25 | Respond Réponse · n8n-nodes-base.respondToWebhook · v1.5 | Renvoie le rapport HTML au demandeur.- Réponse
- Corps =
$('Call WF6 render').first().json.data, Content-Type: text/html; charset=utf-8.
|